From 2b036368cf42059cacb64f8b41bb9562c67c23d7 Mon Sep 17 00:00:00 2001 From: Benjamin ter Kuile Date: Mon, 13 Apr 2015 17:57:06 +0200 Subject: [PATCH] Continue adding product variants --- .../app/components/menu-product.js.coffee | 5 + .../templates/components/menu-product.emblem | 12 +- .../modals/section_add_tables.emblem | 2 +- .../javascripts/translations.js.coffee.erb | 1 - .../app/components/menu-product.js.coffee | 8 +- .../modals/product_variant_select.js.coffee | 5 + .../product_orders_controller.js.coffee | 15 +- .../user/app/models/product-variant.js.coffee | 5 + .../user/app/models/product.js.coffee | 25 +- .../user/app/models/product_order.js.coffee | 7 +- .../templates/components/menu-product.emblem | 2 +- .../app/templates/modals/product_info.emblem | 5 + .../modals/product_variant_select.emblem | 8 + .../components/_menu-products.sass | 9 + .../user/foundation/components/_modal.sass | 3 +- .../components/_product_variants.sass | 4 + .../suppliers/product_variants_controller.rb | 16 +- app/controllers/users/orders_controller.rb | 11 +- app/models/list.rb | 22 +- app/models/product.rb | 2 +- app/models/product_order.rb | 4 +- app/models/product_variant.rb | 1 + app/models/supplier.rb | 1 + app/serializers/product_order_serializer.rb | 2 +- config/locales/en.yml | 2 + config/locales/models.en.yml | 4 + config/locales/models.nl.yml | 4 + config/locales/nl.yml | 2 + ember-data.js | 12183 ---------------- spec/javascript/models/product_test.coffee | 13 + .../models/section-element_test.coffee | 2 +- wip.md | 8 + 32 files changed, 169 insertions(+), 12224 deletions(-) create mode 100644 app/assets/javascripts/user/app/controllers/modals/product_variant_select.js.coffee create mode 100644 app/assets/javascripts/user/app/models/product-variant.js.coffee create mode 100644 app/assets/javascripts/user/app/templates/modals/product_variant_select.emblem create mode 100644 app/assets/stylesheets/user/foundation/components/_product_variants.sass delete mode 100644 ember-data.js create mode 100644 spec/javascript/models/product_test.coffee diff --git a/app/assets/javascripts/supplier/app/components/menu-product.js.coffee b/app/assets/javascripts/supplier/app/components/menu-product.js.coffee index ea1c5a2a..0acdbeba 100644 --- a/app/assets/javascripts/supplier/app/components/menu-product.js.coffee +++ b/app/assets/javascripts/supplier/app/components/menu-product.js.coffee @@ -45,6 +45,11 @@ App.MenuProductComponent = Ember.Component.extend product_variant.rollback() @get('product').rollback() @set 'editMode', false + addProductVariant: -> + product_variant = @get('targetObject.store').createRecord('product_variant') + @get('product.product_variants').addObject product_variant + removeProductVariant: (product_variant)-> + product_variant.destroyRecord() didInsertElement: -> @set 'editMode', true if @get('product.isNew') namePlaceholder: (-> t "attributes.product.name").property() diff --git a/app/assets/javascripts/supplier/app/templates/components/menu-product.emblem b/app/assets/javascripts/supplier/app/templates/components/menu-product.emblem index 38386557..7726e4ef 100644 --- a/app/assets/javascripts/supplier/app/templates/components/menu-product.emblem +++ b/app/assets/javascripts/supplier/app/templates/components/menu-product.emblem @@ -23,9 +23,15 @@ if editMode img src=product.image_src each product_variant in product.product_variants .row - .small-3.medium-2.large-1.columns plus - .small-9.medium-5.large-4.columns.end - = input value=product_variant.name + .small-1.columns   + .small-8.medium-5.large-4.columns= input value=product_variant.name + .small-2.medium-6.large-7.columns + button.remove-product-variant{action "removeProductVariant" product_variant}: span + .row + .small-12.columns + button.add-product-variant{ action "addProductVariant"} + span.icon + = t 'product_variant.add_product_variant' else if showProduct .row diff --git a/app/assets/javascripts/supplier/app/templates/modals/section_add_tables.emblem b/app/assets/javascripts/supplier/app/templates/modals/section_add_tables.emblem index ea3d3885..2fe05afd 100644 --- a/app/assets/javascripts/supplier/app/templates/modals/section_add_tables.emblem +++ b/app/assets/javascripts/supplier/app/templates/modals/section_add_tables.emblem @@ -13,4 +13,4 @@ form.form-horizontal .form-field= number-field numericValue=number_end hr button.modal-close{action "close"}=t 'section.add_tables.modal.close_button' -button.modal-confirm.right{action "addTables"}=t 'section.add_tables.modal.add_button' +button.modal-confirm.right{action "ok"}=t 'section.add_tables.modal.add_button' diff --git a/app/assets/javascripts/translations.js.coffee.erb b/app/assets/javascripts/translations.js.coffee.erb index 3627698c..2e668c6f 100644 --- a/app/assets/javascripts/translations.js.coffee.erb +++ b/app/assets/javascripts/translations.js.coffee.erb @@ -21,7 +21,6 @@ @ttry = (path, vars={})-> @t(path, $.extend(vars, emptyWhenNotFound: true)) - # return translation in the form # Tafel @tspan = (path, vars={}) -> "#{t(path, vars)}" diff --git a/app/assets/javascripts/user/app/components/menu-product.js.coffee b/app/assets/javascripts/user/app/components/menu-product.js.coffee index bbc508ff..898ebeba 100644 --- a/app/assets/javascripts/user/app/components/menu-product.js.coffee +++ b/app/assets/javascripts/user/app/components/menu-product.js.coffee @@ -4,11 +4,13 @@ App.MenuProductComponent = Ember.Component.extend specific_id: (-> "order-product-#{@get('product.id')}").property('product.id') orderProducts: false target: -> @get('parentView.targetObject') + showDescriptionIcon: Ember.computed.or 'product.description', 'product.product_variants.length' + actions: addProduct: (product)-> - if existing = @target().store.all('product_order').find((po)-> po.get('product') == product and not po.get('order')) - existing.increment() + if product.get('product_variants.length') + @target().modal 'product_variant_select', model: product else - @target().store.createRecord 'product_order', product: product, price: product.get('price') + product.addOrderItem() showProductDescription: (product)-> @target().modal 'product_info', model: product, title: product.get('name') diff --git a/app/assets/javascripts/user/app/controllers/modals/product_variant_select.js.coffee b/app/assets/javascripts/user/app/controllers/modals/product_variant_select.js.coffee new file mode 100644 index 00000000..a52b64f8 --- /dev/null +++ b/app/assets/javascripts/user/app/controllers/modals/product_variant_select.js.coffee @@ -0,0 +1,5 @@ +@App.modals.ProductVariantSelectController = @App.modals.BaseController.extend + actions: + chooseProductVariant: (product_variant)-> + @get('model').addOrderItem(product_variant: product_variant.get('name')) + @send 'close' diff --git a/app/assets/javascripts/user/app/controllers/product_orders_controller.js.coffee b/app/assets/javascripts/user/app/controllers/product_orders_controller.js.coffee index 8394f103..0f2c21a5 100644 --- a/app/assets/javascripts/user/app/controllers/product_orders_controller.js.coffee +++ b/app/assets/javascripts/user/app/controllers/product_orders_controller.js.coffee @@ -31,10 +31,17 @@ App.ProductOrdersController = Ember.ArrayController.extend #orders = @store.all('product_order').toArray() #data = @get('product_orders').map( (po)->po.serialize() ) - dataObject = order: {table_id: @get('controllers.table.model.id')} - @get('product_orders').forEach (product_order)-> dataObject['order'][product_order.get('product.id')] = product_order.get('quantity') - Ember.$.post "#{$data_host}/user/orders.json", dataObject, (response) => - @store.pushPayload('order', response) if response.order + dataObject = {table_id: @get('controllers.table.model.id')} + dataObject.product_orders = @get('product_orders').map( (po) -> po.serialize()).toArray() + #@get('product_orders').forEach (product_order)-> dataObject['order'][product_order.get('product.id')] = product_order.get('quantity') + Ember.$.ajax + type: 'POST' + dataType: 'json' + contentType: 'application/json' + url: "#{$data_host}/user/orders.json" + data: JSON.stringify(dataObject) + success: (response) => + @store.pushPayload('order', response) if response.order @transitionToRoute 'active_list' @get('product_orders').invoke 'unloadRecord' diff --git a/app/assets/javascripts/user/app/models/product-variant.js.coffee b/app/assets/javascripts/user/app/models/product-variant.js.coffee new file mode 100644 index 00000000..ca16b0ea --- /dev/null +++ b/app/assets/javascripts/user/app/models/product-variant.js.coffee @@ -0,0 +1,5 @@ +attr = DS.attr +App.ProductVariant = DS.Model.extend + name: attr 'string' + product: DS.belongsTo 'product' + position: attr 'number', defaultValue: 0 diff --git a/app/assets/javascripts/user/app/models/product.js.coffee b/app/assets/javascripts/user/app/models/product.js.coffee index decacdb1..d9326656 100644 --- a/app/assets/javascripts/user/app/models/product.js.coffee +++ b/app/assets/javascripts/user/app/models/product.js.coffee @@ -6,5 +6,26 @@ App.Product = DS.Model.extend position: attr('number', defaultValue: 0) active: attr 'boolean', defaultValue: true image: attr() - product_category: DS.belongsTo('product_category') - product_orders: DS.hasMany('product_order') + product_category: DS.belongsTo('product-category') + product_orders: DS.hasMany('product-order') + product_variants: DS.hasMany 'product-variant' + sorted_product_variants: Ember.computed 'product_variants.@each.name', 'product_variants.@each.position', -> + @get('product_variants').sortBy('position') + + variantsDisplay: Ember.computed 'sorted_product_variants', -> + @get('sorted_product_variants').mapBy('name').join(', ') + + addOrderItem: (options = {})-> + #if existing = @store.all('product_order').find((po)-> po.get('product') == product and not po.get('order')) + if options.product_variant + existing = @get('product_orders').find( (po)-> !po.get('order') and po.get('product_variant') is options.product_variant ) + else + existing = @get('product_orders').find( (po)-> !po.get('order') ) + + if existing + existing.increment() + else + @store.createRecord 'product_order', + product: this + price: @get('price') + product_variant: options.product_variant diff --git a/app/assets/javascripts/user/app/models/product_order.js.coffee b/app/assets/javascripts/user/app/models/product_order.js.coffee index 59947d7f..dfee1518 100644 --- a/app/assets/javascripts/user/app/models/product_order.js.coffee +++ b/app/assets/javascripts/user/app/models/product_order.js.coffee @@ -4,12 +4,17 @@ App.ProductOrder = DS.Model.extend price: attr 'number' product_name: attr('string') product: DS.belongsTo('product') + product_variant: attr('string') order: DS.belongsTo('order') placed: attr('boolean', defaultValue: false) # virtual attribute for new orders to be placed, should be more elegant increment: -> @set('quantity', @get('quantity') + 1) total: (-> @get('quantity') * @get('price')).property('quantity', 'price') - display: (-> "#{@get('quantity')} x #{@get('product.name')}").property('quantity', 'product.name') + display: Ember.computed 'quantity', 'product_variant', 'product.name', -> + disp = "#{@get('quantity')} x #{@get('product.name')}" + if variant = @get('product_variant') + disp = "#{disp} (#{variant})" + disp.htmlSafe() setOrder: (order)-> @set('placed', true) @set('order', order) diff --git a/app/assets/javascripts/user/app/templates/components/menu-product.emblem b/app/assets/javascripts/user/app/templates/components/menu-product.emblem index ef067026..d3a2cb07 100644 --- a/app/assets/javascripts/user/app/templates/components/menu-product.emblem +++ b/app/assets/javascripts/user/app/templates/components/menu-product.emblem @@ -1,4 +1,4 @@ -if product.description +if showDescriptionIcon button.show-product-description{action "showProductDescription" product} span else diff --git a/app/assets/javascripts/user/app/templates/modals/product_info.emblem b/app/assets/javascripts/user/app/templates/modals/product_info.emblem index b4ad3c51..cc2965d2 100644 --- a/app/assets/javascripts/user/app/templates/modals/product_info.emblem +++ b/app/assets/javascripts/user/app/templates/modals/product_info.emblem @@ -1,5 +1,10 @@ if image .right: img src=image.small alt="" p==description +if product_variants + h4= t 'models.plural.product_variant' + ul + each product_variant in product_variants + li= product_variant.name hr button{action "close"}= t 'modal.info.close' diff --git a/app/assets/javascripts/user/app/templates/modals/product_variant_select.emblem b/app/assets/javascripts/user/app/templates/modals/product_variant_select.emblem new file mode 100644 index 00000000..d9bd6851 --- /dev/null +++ b/app/assets/javascripts/user/app/templates/modals/product_variant_select.emblem @@ -0,0 +1,8 @@ +if model.image + .right: img src=model.image.small alt="" +each product_variant in product_variants + .row + .small-8.columns: a{action "chooseProductVariant" product_variant}= product_variant.name + .small-4.columns: a.choose-product-variant-button{action "chooseProductVariant" product_variant}= t 'product_variant.choose' +hr +button.modal-close{action "close"}= t 'modal.info.close' diff --git a/app/assets/stylesheets/supplier/foundation1/components/_menu-products.sass b/app/assets/stylesheets/supplier/foundation1/components/_menu-products.sass index afc701f8..e00ab57f 100644 --- a/app/assets/stylesheets/supplier/foundation1/components/_menu-products.sass +++ b/app/assets/stylesheets/supplier/foundation1/components/_menu-products.sass @@ -90,6 +90,15 @@ @extend .fa @extend .fa-lg @extend .fa-trash + .remove-product-variant + +push-button($bg: $alert-color, $padding: 5px) + span + @extend .fa, .fa-trash + .add-product-variant + +button($bg: $success-color, $padding: $button-tny) + .icon + @extend .fa, .fa-plus + margin-right: 6px .error color: $alert-color input diff --git a/app/assets/stylesheets/user/foundation/components/_modal.sass b/app/assets/stylesheets/user/foundation/components/_modal.sass index 94b84e97..32adaf87 100644 --- a/app/assets/stylesheets/user/foundation/components/_modal.sass +++ b/app/assets/stylesheets/user/foundation/components/_modal.sass @@ -1,7 +1,8 @@ .modal margin: auto margin-top: 90px - width: 300px + width: 90% + max-width: 756px background-color: #fff padding: 1em z-index: 9085 diff --git a/app/assets/stylesheets/user/foundation/components/_product_variants.sass b/app/assets/stylesheets/user/foundation/components/_product_variants.sass new file mode 100644 index 00000000..bc00f533 --- /dev/null +++ b/app/assets/stylesheets/user/foundation/components/_product_variants.sass @@ -0,0 +1,4 @@ +.choose-product-variant-button + +button($padding: $button-tny) + margin: 0 + margin-bottom: 6px diff --git a/app/controllers/suppliers/product_variants_controller.rb b/app/controllers/suppliers/product_variants_controller.rb index 625e8607..2f2d8c73 100644 --- a/app/controllers/suppliers/product_variants_controller.rb +++ b/app/controllers/suppliers/product_variants_controller.rb @@ -24,6 +24,13 @@ module Suppliers # POST /product_variants # POST /product_variants.json def create + @product_variant = ProductVariant.new(product_variant_params) + @product_variant.supplier = current_supplier + if @product_variant.save + render json: @product_variant + else + render json: {errors: @product_variant.errors}, status: :unprocessable_entity + end end # PUT /product_variants/1 @@ -44,13 +51,10 @@ module Suppliers # DELETE /product_variants/1 # DELETE /product_variants/1.json def destroy - @product_variant = ProductVariant.find_by_supplier_id_and_id!(current_supplier.id, params[:id]) + #@product_variant = ProductVariant.find_by_supplier_id_and_id!(current_supplier.id, params[:id]) + @product_variant = ProductVariant.find(params[:id]) @product_variant.destroy - - respond_to do |format| - format.html { redirect_to suppliers_product_variants_url, notice: t('action.destroy.successfull', model: ProductVariant.model_name.human) } - format.json { head :no_content } - end + render json: {} end private diff --git a/app/controllers/users/orders_controller.rb b/app/controllers/users/orders_controller.rb index e1e8da1f..86067cb1 100644 --- a/app/controllers/users/orders_controller.rb +++ b/app/controllers/users/orders_controller.rb @@ -6,9 +6,6 @@ module Users # POST /user/orders def create # render json: {}, status: :unprocessable_entity and return unless params[:order].present? && params[:order][:product_orders].present? - # converted_order = params[:order][:product_orders].each_with_object({}){|po, o| o[po[:product_id]] = po[:quantity] } - converted_order = params[:order] - table_id = params[:order].delete('table_id') if list = current_user.active_list render json: {}, status: :not_acceptable and return unless list.supplier.open? else @@ -16,8 +13,8 @@ module Users #NOTE: security bug here!!!!!! # - supplier.open? # - etc.... - render json: {}, status: :unprocessable_entity and return unless table_id.present? - table = Table.find(table_id) + render json: {}, status: :unprocessable_entity and return unless params[:table_id].present? + table = Table.find(params[:table_id]) render json: {}, status: :not_acceptable and return unless table.supplier.open? if table.occupied? @@ -27,8 +24,8 @@ module Users list = List.from_table( table, current_user ) end - order = list.place_order products: converted_order, user: current_user - render json: order, serializer: OrderSerializer + order = list.place_order product_orders: params[:product_orders], user: current_user + render json: order, serializer: OrderSerializer #render nothing: true end end diff --git a/app/models/list.rb b/app/models/list.rb index 15933655..f9b82de4 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -274,16 +274,24 @@ class List state == 'active' end - def place_order(products: {}, user: nil, employee: nil) - return false unless products.any? + def place_order(product_orders: [], user: nil, employee: nil) + return false unless product_orders.any? order = Order.create list: self, supplier: supplier, user: user, employee: employee, section_id: section_id, table_id: table_id return unless order.id orders_placed_count = supplier.increment_orders_placed_count! - loaded_products = self.class.database.load_document products.keys - products.each do |product_id, quantity| - product = loaded_products.find{|p| p.id == product_id} # to get the price - if quantity.to_i > 0 - ProductOrder.create(order: order, product_id: product_id, quantity: quantity, price: product.price, product_name: product.name) + loaded_products = self.class.database.load_document product_orders.map{|po| po['product_id']} + product_orders.each do |product_order| + next unless product = loaded_products.find{|p| p.id == product_order['product_id']} # to get the price + quantity = product_order['quantity'].to_i + if quantity > 0 + ProductOrder.create( + order: order, + product_id: product.id, + quantity: quantity, + price: product.price, + product_name: product.name, + product_variant: product_order['product_variant'] + ) end end set_price diff --git a/app/models/product.rb b/app/models/product.rb index 3a051a00..473c740d 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -15,7 +15,7 @@ class Product #has_and_belongs_to_many :product_categories, storing_keys: false belongs_to :supplier # direct! category is an aid has_many :product_orders - has_many :product_variants + has_many :product_variants, dependent: :destroy attr_protected :supplier_id diff --git a/app/models/product_order.rb b/app/models/product_order.rb index af8d4f9e..02969bee 100644 --- a/app/models/product_order.rb +++ b/app/models/product_order.rb @@ -5,6 +5,7 @@ class ProductOrder property :quantity, type: Fixnum property :price, type: Float property :product_name + property :product_variant belongs_to :product belongs_to :order @@ -14,7 +15,8 @@ class ProductOrder # Getter for product name. If a supplier deletes a product, that has product_orders, the product # will become nil. This method should handle this case. + alias_method :direct_product_name, :product_name def product_name - product.try(:name) || '[deleted]' + direct_product_name.presence || product.try(:name) || '[deleted]' end end diff --git a/app/models/product_variant.rb b/app/models/product_variant.rb index b76a1a29..8ed85c33 100644 --- a/app/models/product_variant.rb +++ b/app/models/product_variant.rb @@ -4,4 +4,5 @@ class ProductVariant property :name property :position, type: Fixnum, default: 0 belongs_to :product + belongs_to :supplier end diff --git a/app/models/supplier.rb b/app/models/supplier.rb index 012e17b8..9fd99bc4 100644 --- a/app/models/supplier.rb +++ b/app/models/supplier.rb @@ -35,6 +35,7 @@ class Supplier #has_many :orders, through: :lists has_many :products, dependent: :destroy + has_many :product_variants has_many :product_categories, dependent: :destroy has_many :tables, dependent: :destroy has_many :lists, dependent: :destroy diff --git a/app/serializers/product_order_serializer.rb b/app/serializers/product_order_serializer.rb index d07f6385..38990d3f 100644 --- a/app/serializers/product_order_serializer.rb +++ b/app/serializers/product_order_serializer.rb @@ -1,4 +1,4 @@ # Used for user ember1 class ProductOrderSerializer < Qwaiter::Serializer - attributes :order_id, :product_id, :quantity, :price, :product_name + attributes :order_id, :product_id, :quantity, :price, :product_name, :product_variant end diff --git a/config/locales/en.yml b/config/locales/en.yml index 3f33567f..788fc460 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -78,3 +78,5 @@ en: selected_products: clear: Clear order: Order + product_variant: + add_product_variant: Add ${models.product_variant} diff --git a/config/locales/models.en.yml b/config/locales/models.en.yml index f4ebee7a..d20a9da7 100644 --- a/config/locales/models.en.yml +++ b/config/locales/models.en.yml @@ -9,6 +9,7 @@ en: product: Product order: Order product_category: Product category + product_variant: Variant section: Section join_request: Join request user_feedback: User feedback @@ -26,6 +27,7 @@ en: product: Products order: Orders product_category: Product categories + product_variant: Variants section: Sections join_request: Join requests user_feedback: User feedbacks @@ -52,6 +54,8 @@ en: visible: Visible? created_at: Created image: Image + product_variant: + name: Name list: created_at: Created state: Status diff --git a/config/locales/models.nl.yml b/config/locales/models.nl.yml index 3ecc1efc..19fc4159 100644 --- a/config/locales/models.nl.yml +++ b/config/locales/models.nl.yml @@ -9,6 +9,7 @@ nl: product: Product order: Bestelling product_category: Product categorie + product_variant: Variant section: Afdeling join_request: Deelname verzoek employee: Werknemer @@ -25,6 +26,7 @@ nl: product: Producten order: Bestellingen product_category: Product categorieen + product_variant: Varianten section: Afdelingen join_request: Deelname verzoeken employee: Werknemers @@ -50,6 +52,8 @@ nl: active: "Actief?" created_at: Aangemaakt image: Afbeelding + product_variant: + name: Naam list: created_at: Aangemaakt state: Status diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 7475c843..97b26880 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -76,6 +76,8 @@ nl: selected_products: clear: Leegmaken order: Bestellen + product_variant: + add_product_variant: ${models.product_variant} toevoegen views: pagination: first: "« Eerste" diff --git a/ember-data.js b/ember-data.js deleted file mode 100644 index ca63f1f2..00000000 --- a/ember-data.js +++ /dev/null @@ -1,12183 +0,0 @@ -// Fetched from channel: canary, with url http://builds.emberjs.com/canary/ember-data.js -// Fetched on: 2014-07-01T13:19:27Z -/*! - * @overview Ember Data - * @copyright Copyright 2011-2014 Tilde Inc. and contributors. - * Portions Copyright 2011 LivingSocial Inc. - * @license Licensed under MIT license (see license.js) - * @version 1.0.0-beta.9+canary.9a5cc255 - */ -(function(global) { -var define, requireModule, require, requirejs; - -(function() { - var registry = {}, seen = {}; - - define = function(name, deps, callback) { - registry[name] = { deps: deps, callback: callback }; - }; - - requirejs = require = requireModule = function(name) { - requirejs._eak_seen = registry; - - if (seen[name]) { return seen[name]; } - seen[name] = {}; - - if (!registry[name]) { - throw new Error("Could not find module " + name); - } - - var mod = registry[name], - deps = mod.deps, - callback = mod.callback, - reified = [], - exports; - - for (var i=0, l=deps.length; i "famous_people" - ``` - - @method pathForType - @param {String} type - @return String - */ - pathForType: function(type) { - var decamelized = decamelize(type); - var underscored = underscore(decamelized); - return pluralize(underscored); - }, - - /** - The ActiveModelAdapter overrides the `ajaxError` method - to return a DS.InvalidError for all 422 Unprocessable Entity - responses. - - A 422 HTTP response from the server generally implies that the request - was well formed but the API was unable to process it because the - content was not semantically correct or meaningful per the API. - - For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918 - https://tools.ietf.org/html/rfc4918#section-11.2 - - @method ajaxError - @param jqXHR - @return error - */ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); - - if (jqXHR && jqXHR.status === 422) { - var response = Ember.$.parseJSON(jqXHR.responseText), - errors = {}; - - if (response.errors !== undefined) { - var jsonErrors = response.errors; - - forEach(Ember.keys(jsonErrors), function(key) { - errors[Ember.String.camelize(key)] = jsonErrors[key]; - }); - } - - return new InvalidError(errors); - } else { - return error; - } - } - }); - - __exports__["default"] = ActiveModelAdapter; - }); -define("activemodel-adapter/lib/system/active_model_serializer", - ["../../../ember-inflector/lib/main","../../../ember-data/lib/serializers/rest_serializer","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var singularize = __dependency1__.singularize; - var RESTSerializer = __dependency2__["default"]; - /** - @module ember-data - */ - - var get = Ember.get, - forEach = Ember.EnumerableUtils.forEach, - camelize = Ember.String.camelize, - capitalize = Ember.String.capitalize, - decamelize = Ember.String.decamelize, - underscore = Ember.String.underscore; - /** - The ActiveModelSerializer is a subclass of the RESTSerializer designed to integrate - with a JSON API that uses an underscored naming convention instead of camelCasing. - It has been designed to work out of the box with the - [active_model_serializers](http://github.com/rails-api/active_model_serializers) - Ruby gem. This Serializer expects specific settings using ActiveModel::Serializers, - `embed :ids, include: true` which sideloads the records. - - This serializer extends the DS.RESTSerializer by making consistent - use of the camelization, decamelization and pluralization methods to - normalize the serialized JSON into a format that is compatible with - a conventional Rails backend and Ember Data. - - ## JSON Structure - - The ActiveModelSerializer expects the JSON returned from your server - to follow the REST adapter conventions substituting underscored keys - for camelcased ones. - - ### Conventional Names - - Attribute names in your JSON payload should be the underscored versions of - the attributes in your Ember.js models. - - For example, if you have a `Person` model: - - ```js - App.FamousPerson = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string') - }); - ``` - - The JSON returned should look like this: - - ```js - { - "famous_person": { - "id": 1, - "first_name": "Barack", - "last_name": "Obama", - "occupation": "President" - } - } - ``` - - Let's imagine that `Occupation` is just another model: - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.belongsTo('occupation') - }); - - App.Occupation = DS.Model.extend({ - name: DS.attr('string'), - salary: DS.attr('number'), - people: DS.hasMany('person') - }); - ``` - - The JSON needed to avoid extra server calls, should look like this: - - ```js - { - "people": [{ - "id": 1, - "first_name": "Barack", - "last_name": "Obama", - "occupation_id": 1 - }], - - "occupations": [{ - "id": 1, - "name": "President", - "salary": 100000, - "person_ids": [1] - }] - } - ``` - - @class ActiveModelSerializer - @namespace DS - @extends DS.RESTSerializer - */ - var ActiveModelSerializer = RESTSerializer.extend({ - // SERIALIZE - - /** - Converts camelCased attributes to underscored when serializing. - - @method keyForAttribute - @param {String} attribute - @return String - */ - keyForAttribute: function(attr) { - return decamelize(attr); - }, - - /** - Underscores relationship names and appends "_id" or "_ids" when serializing - relationship keys. - - @method keyForRelationship - @param {String} key - @param {String} kind - @return String - */ - keyForRelationship: function(key, kind) { - key = decamelize(key); - if (kind === "belongsTo") { - return key + "_id"; - } else if (kind === "hasMany") { - return singularize(key) + "_ids"; - } else { - return key; - } - }, - - /* - Does not serialize hasMany relationships by default. - */ - serializeHasMany: Ember.K, - - /** - Underscores the JSON root keys when serializing. - - @method serializeIntoHash - @param {Object} hash - @param {subclass of DS.Model} type - @param {DS.Model} record - @param {Object} options - */ - serializeIntoHash: function(data, type, record, options) { - var root = underscore(decamelize(type.typeKey)); - data[root] = this.serialize(record, options); - }, - - /** - Serializes a polymorphic type as a fully capitalized model name. - - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param relationship - */ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key, - belongsTo = get(record, key); - - if (belongsTo) { - key = this.keyForAttribute(key); - json[key + "_type"] = capitalize(belongsTo.constructor.typeKey); - } - }, - - // EXTRACT - - /** - Add extra step to `DS.RESTSerializer.normalize` so links are normalized. - - If your payload looks like: - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "flagged_comments": "api/comments/flagged" } - } - } - ``` - - The normalized version would look like this - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "flaggedComments": "api/comments/flagged" } - } - } - ``` - - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @param {String} prop - @return Object - */ - - normalize: function(type, hash, prop) { - this.normalizeLinks(hash); - - return this._super(type, hash, prop); - }, - - /** - Convert `snake_cased` links to `camelCase` - - @method normalizeLinks - @param {Object} data - */ - - normalizeLinks: function(data){ - if (data.links) { - var links = data.links; - - for (var link in links) { - var camelizedLink = camelize(link); - - if (camelizedLink !== link) { - links[camelizedLink] = links[link]; - delete links[link]; - } - } - } - }, - - /** - Normalize the polymorphic type from the JSON. - - Normalize: - ```js - { - id: "1" - minion: { type: "evil_minion", id: "12"} - } - ``` - - To: - ```js - { - id: "1" - minion: { type: "evilMinion", id: "12"} - } - ``` - - @method normalizeRelationships - @private - */ - normalizeRelationships: function(type, hash) { - - if (this.keyForRelationship) { - type.eachRelationship(function(key, relationship) { - var payloadKey, payload; - if (relationship.options.polymorphic) { - payloadKey = this.keyForAttribute(key); - payload = hash[payloadKey]; - if (payload && payload.type) { - payload.type = this.typeForRoot(payload.type); - } else if (payload && relationship.kind === "hasMany") { - var self = this; - forEach(payload, function(single) { - single.type = self.typeForRoot(single.type); - }); - } - } else { - payloadKey = this.keyForRelationship(key, relationship.kind); - payload = hash[payloadKey]; - } - - hash[key] = payload; - - if (key !== payloadKey) { - delete hash[payloadKey]; - } - }, this); - } - } - }); - - __exports__["default"] = ActiveModelSerializer; - }); -define("activemodel-adapter/lib/system/embedded_records_mixin", - ["../../../ember-inflector/lib/main","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var get = Ember.get; - var forEach = Ember.EnumerableUtils.forEach; - var camelize = Ember.String.camelize; - - var pluralize = __dependency1__.pluralize; - - /** - ## Using Embedded Records - - `DS.EmbeddedRecordsMixin` supports serializing embedded records. - - To set up embedded records, include the mixin when extending a serializer - then define and configure embedded (model) relationships. - - Below is an example of a per-type serializer ('post' type). - - ```js - App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - author: {embedded: 'always'}, - comments: {serialize: 'ids'} - } - }) - ``` - - The `attrs` option for a resource `{embedded: 'always'}` is shorthand for: - - ```js - {serialize: 'records', deserialize: 'records'} - ``` - - ### Configuring Attrs - - A resource's `attrs` option may be set to use `ids`, `records` or `no` for the - `serialize` and `deserialize` settings. - - The `attrs` property can be set on the ApplicationSerializer or a per-type - serializer. - - In the case where embedded JSON is expected while extracting a payload (reading) - the setting is `deserialize: 'records'`, there is no need to use `ids` when - extracting as that is the default behavior without this mixin if you are using - the vanilla ActiveModelAdapter. Likewise, to embed JSON in the payload while - serializing `serialize: 'records'` is the setting to use. There is an option of - not embedding JSON in the serialized payload by using `serialize: 'ids'`. If you - do not want the relationship sent at all, you can use `serialize: 'no'`. - - - ### ActiveModelSerializer defaults - If you do not overwrite `attrs` for a specific relationship, the `ActiveModelSerializer` - will behave in the following way: - - BelongsTo: `{serialize:'id', deserialize:'id'}` - HasMany: `{serialize:'no', deserialize:'ids'}` - - ### Model Relationships - - Embedded records must have a model defined to be extracted and serialized. - - To successfully extract and serialize embedded records the model relationships - must be setup correcty See the - [defining relationships](/guides/models/defining-models/#toc_defining-relationships) - section of the **Defining Models** guide page. - - Records without an `id` property are not considered embedded records, model - instances must have an `id` property to be used with Ember Data. - - ### Example JSON payloads, Models and Serializers - - **When customizing a serializer it is imporant to grok what the cusomizations - are, please read the docs for the methods this mixin provides, in case you need - to modify to fit your specific needs.** - - For example review the docs for each method of this mixin: - - * [extractArray](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_extractArray) - * [extractSingle](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_extractSingle) - * [serializeBelongsTo](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeBelongsTo) - * [serializeHasMany](/api/data/classes/DS.EmbeddedRecordsMixin.html#method_serializeHasMany) - - @class EmbeddedRecordsMixin - @namespace DS - */ - var EmbeddedRecordsMixin = Ember.Mixin.create({ - - /** - Serialize `belongsTo` relationship when it is configured as an embedded object. - - This example of an author model belongs to a post model: - - ```js - Post = DS.Model.extend({ - title: DS.attr('string'), - body: DS.attr('string'), - author: DS.belongsTo('author') - }); - - Author = DS.Model.extend({ - name: DS.attr('string'), - post: DS.belongsTo('post') - }); - ``` - - Use a custom (type) serializer for the post model to configure embedded author - - ```js - App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - author: {embedded: 'always'} - } - }) - ``` - - A payload with an attribute configured for embedded records can serialize - the records together under the root attribute's payload: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "author": { - "id": "2" - "name": "dhh" - } - } - } - ``` - - @method serializeBelongsTo - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeBelongsTo: function(record, json, relationship) { - var attr = relationship.key; - var attrs = this.get('attrs'); - if (noSerializeOptionSpecified(attrs, attr)) { - this._super(record, json, relationship); - return; - } - var includeIds = hasSerializeIdsOption(attrs, attr); - var includeRecords = hasSerializeRecordsOption(attrs, attr); - var embeddedRecord = record.get(attr); - if (includeIds) { - key = this.keyForRelationship(attr, relationship.kind); - if (!embeddedRecord) { - json[key] = null; - } else { - json[key] = get(embeddedRecord, 'id'); - } - } else if (includeRecords) { - var key = this.keyForRelationship(attr); - if (!embeddedRecord) { - json[key] = null; - } else { - json[key] = embeddedRecord.serialize({includeId: true}); - this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, json[key]); - } - } - }, - - /** - Serialize `hasMany` relationship when it is configured as embedded objects. - - This example of a post model has many comments: - - ```js - Post = DS.Model.extend({ - title: DS.attr('string'), - body: DS.attr('string'), - comments: DS.hasMany('comment') - }); - - Comment = DS.Model.extend({ - body: DS.attr('string'), - post: DS.belongsTo('post') - }); - ``` - - Use a custom (type) serializer for the post model to configure embedded comments - - ```js - App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - comments: {embedded: 'always'} - } - }) - ``` - - A payload with an attribute configured for embedded records can serialize - the records together under the root attribute's payload: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "body": "I want this for my ORM, I want that for my template language..." - "comments": [{ - "id": "1", - "body": "Rails is unagi" - }, { - "id": "2", - "body": "Omakase O_o" - }] - } - } - ``` - - The attrs options object can use more specific instruction for extracting and - serializing. When serializing, an option to embed `ids` or `records` can be set. - When extracting the only option is `records`. - - So `{embedded: 'always'}` is shorthand for: - `{serialize: 'records', deserialize: 'records'}` - - To embed the `ids` for a related object (using a hasMany relationship): - - ```js - App.PostSerializer = DS.RESTSerializer.extend(DS.EmbeddedRecordsMixin, { - attrs: { - comments: {serialize: 'ids', deserialize: 'records'} - } - }) - ``` - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "body": "I want this for my ORM, I want that for my template language..." - "comments": ["1", "2"] - } - } - ``` - - @method serializeHasMany - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeHasMany: function(record, json, relationship) { - var attr = relationship.key; - var attrs = this.get('attrs'); - if (noSerializeOptionSpecified(attrs, attr)) { - this._super(record, json, relationship); - return; - } - var includeIds = hasSerializeIdsOption(attrs, attr); - var includeRecords = hasSerializeRecordsOption(attrs, attr); - var key; - if (includeIds) { - key = this.keyForRelationship(attr, relationship.kind); - json[key] = get(record, attr).mapBy('id'); - } else if (includeRecords) { - key = getKeyForAttribute.call(this, attr); - json[key] = get(record, attr).map(function(embeddedRecord) { - var serializedEmbeddedRecord = embeddedRecord.serialize({includeId: true}); - this.removeEmbeddedForeignKey(record, embeddedRecord, relationship, serializedEmbeddedRecord); - return serializedEmbeddedRecord; - }, this); - } - }, - - /** - When serializing an embedded record, modify the property (in the json payload) - that refers to the parent record (foreign key for relationship). - - Serializing a `belongsTo` relationship removes the property that refers to the - parent record - - Serializing a `hasMany` relationship does not remove the property that refers to - the parent record. - - @method removeEmbeddedForeignKey - @param {DS.Model} record - @param {DS.Model} embeddedRecord - @param {Object} relationship - @param {Object} json - */ - removeEmbeddedForeignKey: function (record, embeddedRecord, relationship, json) { - if (relationship.kind === 'hasMany') { - return; - } else if (relationship.kind === 'belongsTo') { - var parentRecord = record.constructor.inverseFor(relationship.key); - if (parentRecord) { - var name = parentRecord.name; - var embeddedSerializer = this.store.serializerFor(embeddedRecord.constructor); - var parentKey = embeddedSerializer.keyForRelationship(name, parentRecord.kind); - if (parentKey) { - delete json[parentKey]; - } - } - } - }, - - /** - Extract an embedded object from the payload for a single object - and add the object in the compound document (side-loaded) format instead. - - A payload with an attribute configured for embedded records needs to be extracted: - - ```js - { - "post": { - "id": 1 - "title": "Rails is omakase", - "author": { - "id": 2 - "name": "dhh" - } - "comments": [] - } - } - ``` - - Ember Data is expecting a payload with a compound document (side-loaded) like: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "author": "2" - "comments": [] - }, - "authors": [{ - "id": "2" - "post": "1" - "name": "dhh" - }] - "comments": [] - } - ``` - - The payload's `author` attribute represents an object with a `belongsTo` relationship. - The `post` attribute under `author` is the foreign key with the id for the post - - @method extractSingle - @param {DS.Store} store - @param {subclass of DS.Model} primaryType - @param {Object} payload - @param {String} recordId - @return Object the primary response to the original request - */ - extractSingle: function(store, primaryType, payload, recordId) { - var key = primaryType.typeKey; - var root = getKeyForAttribute.call(this, key); - var partial = payload[root]; - - updatePayloadWithEmbedded(this, store, primaryType, payload, partial); - - return this._super(store, primaryType, payload, recordId); - }, - - /** - Extract embedded objects in an array when an attr is configured for embedded, - and add them as side-loaded objects instead. - - A payload with an attr configured for embedded records needs to be extracted: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "comments": [{ - "id": "1", - "body": "Rails is unagi" - }, { - "id": "2", - "body": "Omakase O_o" - }] - } - } - ``` - - Ember Data is expecting a payload with compound document (side-loaded) like: - - ```js - { - "post": { - "id": "1" - "title": "Rails is omakase", - "comments": ["1", "2"] - }, - "comments": [{ - "id": "1", - "body": "Rails is unagi" - }, { - "id": "2", - "body": "Omakase O_o" - }] - } - ``` - - The payload's `comments` attribute represents records in a `hasMany` relationship - - @method extractArray - @param {DS.Store} store - @param {subclass of DS.Model} primaryType - @param {Object} payload - @return {Array} The primary array that was returned in response - to the original query. - */ - extractArray: function(store, primaryType, payload) { - var key = primaryType.typeKey; - var root = getKeyForAttribute.call(this, key); - var partials = payload[pluralize(root)]; - - forEach(partials, function(partial) { - updatePayloadWithEmbedded(this, store, primaryType, payload, partial); - }, this); - - return this._super(store, primaryType, payload); - } - }); - - // `keyForAttribute` is optional but may be defined when extending a serializer prototype - var getKeyForAttribute = function(attr) { - return (this.keyForAttribute) ? this.keyForAttribute(attr) : attr; - }; - - // checks config for attrs option to embedded (always) - serialize and deserialize - function hasEmbeddedAlwaysOption(attrs, attr) { - var option = attrsOption(attrs, attr); - return option && option.embedded === 'always'; - } - - // checks config for attrs option to serialize ids - function hasSerializeRecordsOption(attrs, attr) { - var alwaysEmbed = hasEmbeddedAlwaysOption(attrs, attr); - var option = attrsOption(attrs, attr); - return alwaysEmbed || (option && (option.serialize === 'records')); - } - - // checks config for attrs option to serialize records - function hasSerializeIdsOption(attrs, attr) { - var option = attrsOption(attrs, attr); - return option && (option.serialize === 'ids' || option.serialize === 'id'); - } - - // checks config for attrs option to serialize records - function noSerializeOptionSpecified(attrs, attr) { - var option = attrsOption(attrs, attr); - var serializeRecords = hasSerializeRecordsOption(attrs, attr); - var serializeIds = hasSerializeIdsOption(attrs, attr); - return !(option && (option.serialize || option.embedded)); - } - - // checks config for attrs option to deserialize records - // a defined option object for a resource is treated the same as - // `deserialize: 'records'` - function hasDeserializeRecordsOption(attrs, attr) { - var alwaysEmbed = hasEmbeddedAlwaysOption(attrs, attr); - var option = attrsOption(attrs, attr); - var hasSerializingOption = option && (option.deserialize || option.serialize); - return alwaysEmbed || hasSerializingOption /* option.deserialize === 'records' */; - } - - function attrsOption(attrs, attr) { - return attrs && (attrs[Ember.String.camelize(attr)] || attrs[attr]); - } - - // chooses a relationship kind to branch which function is used to update payload - // does not change payload if attr is not embedded - function updatePayloadWithEmbedded(serializer, store, type, payload, partial) { - var attrs = get(serializer, 'attrs'); - - if (!attrs) { - return; - } - type.eachRelationship(function(key, relationship) { - if (hasDeserializeRecordsOption(attrs, key)) { - if (relationship.kind === "hasMany") { - updatePayloadWithEmbeddedHasMany(serializer, store, key, relationship, payload, partial); - } - if (relationship.kind === "belongsTo") { - updatePayloadWithEmbeddedBelongsTo(serializer, store, key, relationship, payload, partial); - } - } - }); - } - - // handles embedding for `hasMany` relationship - function updatePayloadWithEmbeddedHasMany(serializer, store, primaryType, relationship, payload, partial) { - var embeddedSerializer = store.serializerFor(relationship.type.typeKey); - var primaryKey = get(serializer, 'primaryKey'); - var attr = relationship.type.typeKey; - // underscore forces the embedded records to be side loaded. - // it is needed when main type === relationship.type - var embeddedTypeKey = '_' + serializer.typeForRoot(relationship.type.typeKey); - var expandedKey = serializer.keyForRelationship(primaryType, relationship.kind); - var attribute = getKeyForAttribute.call(serializer, primaryType); - var ids = []; - - if (!partial[attribute]) { - return; - } - - payload[embeddedTypeKey] = payload[embeddedTypeKey] || []; - - forEach(partial[attribute], function(data) { - var embeddedType = store.modelFor(attr); - updatePayloadWithEmbedded(embeddedSerializer, store, embeddedType, payload, data); - ids.push(data[primaryKey]); - payload[embeddedTypeKey].push(data); - }); - - partial[expandedKey] = ids; - delete partial[attribute]; - } - - // handles embedding for `belongsTo` relationship - function updatePayloadWithEmbeddedBelongsTo(serializer, store, primaryType, relationship, payload, partial) { - var attrs = serializer.get('attrs'); - - if (!attrs || - !(hasDeserializeRecordsOption(attrs, Ember.String.camelize(primaryType)) || - hasDeserializeRecordsOption(attrs, primaryType))) { - return; - } - var attr = relationship.type.typeKey; - var _serializer = store.serializerFor(relationship.type.typeKey); - var primaryKey = get(_serializer, 'primaryKey'); - var embeddedTypeKey = Ember.String.pluralize(attr); // TODO don't use pluralize - var expandedKey = _serializer.keyForRelationship(primaryType, relationship.kind); - var attribute = getKeyForAttribute.call(_serializer, primaryType); - - if (!partial[attribute]) { - return; - } - payload[embeddedTypeKey] = payload[embeddedTypeKey] || []; - var embeddedType = store.modelFor(relationship.type.typeKey); - // Recursive call for nested record - updatePayloadWithEmbedded(_serializer, store, embeddedType, payload, partial[attribute]); - partial[expandedKey] = partial[attribute].id; - // Need to move an embedded `belongsTo` object into a pluralized collection - payload[embeddedTypeKey].push(partial[attribute]); - // Need a reference to the parent so relationship works between both `belongsTo` records - partial[attribute][relationship.parentType.typeKey + '_id'] = partial.id; - delete partial[attribute]; - } - - __exports__["default"] = EmbeddedRecordsMixin; - }); -define("ember-data/lib/adapters", - ["./adapters/fixture_adapter","./adapters/rest_adapter","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var FixtureAdapter = __dependency1__["default"]; - var RESTAdapter = __dependency2__["default"]; - - __exports__.RESTAdapter = RESTAdapter; - __exports__.FixtureAdapter = FixtureAdapter; - }); -define("ember-data/lib/adapters/fixture_adapter", - ["../system/adapter","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var get = Ember.get; - var fmt = Ember.String.fmt; - var indexOf = Ember.EnumerableUtils.indexOf; - - var counter = 0; - - var Adapter = __dependency1__["default"]; - - /** - `DS.FixtureAdapter` is an adapter that loads records from memory. - It's primarily used for development and testing. You can also use - `DS.FixtureAdapter` while working on the API but is not ready to - integrate yet. It is a fully functioning adapter. All CRUD methods - are implemented. You can also implement query logic that a remote - system would do. It's possible to develop your entire application - with `DS.FixtureAdapter`. - - For information on how to use the `FixtureAdapter` in your - application please see the [FixtureAdapter - guide](/guides/models/the-fixture-adapter/). - - @class FixtureAdapter - @namespace DS - @extends DS.Adapter - */ - __exports__["default"] = Adapter.extend({ - // by default, fixtures are already in normalized form - serializer: null, - - /** - If `simulateRemoteResponse` is `true` the `FixtureAdapter` will - wait a number of milliseconds before resolving promises with the - fixture values. The wait time can be configured via the `latency` - property. - - @property simulateRemoteResponse - @type {Boolean} - @default true - */ - simulateRemoteResponse: true, - - /** - By default the `FixtureAdapter` will simulate a wait of the - `latency` milliseconds before resolving promises with the fixture - values. This behavior can be turned off via the - `simulateRemoteResponse` property. - - @property latency - @type {Number} - @default 50 - */ - latency: 50, - - /** - Implement this method in order to provide data associated with a type - - @method fixturesForType - @param {Subclass of DS.Model} type - @return {Array} - */ - fixturesForType: function(type) { - if (type.FIXTURES) { - var fixtures = Ember.A(type.FIXTURES); - return fixtures.map(function(fixture){ - var fixtureIdType = typeof fixture.id; - if(fixtureIdType !== "number" && fixtureIdType !== "string"){ - throw new Error(fmt('the id property must be defined as a number or string for fixture %@', [fixture])); - } - fixture.id = fixture.id + ''; - return fixture; - }); - } - return null; - }, - - /** - Implement this method in order to query fixtures data - - @method queryFixtures - @param {Array} fixture - @param {Object} query - @param {Subclass of DS.Model} type - @return {Promise|Array} - */ - queryFixtures: function(fixtures, query, type) { - Ember.assert('Not implemented: You must override the DS.FixtureAdapter::queryFixtures method to support querying the fixture store.'); - }, - - /** - @method updateFixtures - @param {Subclass of DS.Model} type - @param {Array} fixture - */ - updateFixtures: function(type, fixture) { - if(!type.FIXTURES) { - type.FIXTURES = []; - } - - var fixtures = type.FIXTURES; - - this.deleteLoadedFixture(type, fixture); - - fixtures.push(fixture); - }, - - /** - Implement this method in order to provide json for CRUD methods - - @method mockJSON - @param {Subclass of DS.Model} type - @param {DS.Model} record - */ - mockJSON: function(store, type, record) { - return store.serializerFor(type).serialize(record, { includeId: true }); - }, - - /** - @method generateIdForRecord - @param {DS.Store} store - @param {DS.Model} record - @return {String} id - */ - generateIdForRecord: function(store) { - return "fixture-" + counter++; - }, - - /** - @method find - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} id - @return {Promise} promise - */ - find: function(store, type, id) { - var fixtures = this.fixturesForType(type), - fixture; - - Ember.assert("Unable to find fixtures for model type "+type.toString() +". If you're defining your fixtures using `Model.FIXTURES = ...`, please change it to `Model.reopenClass({ FIXTURES: ... })`.", fixtures); - - if (fixtures) { - fixture = Ember.A(fixtures).findBy('id', id); - } - - if (fixture) { - return this.simulateRemoteCall(function() { - return fixture; - }, this); - } - }, - - /** - @method findMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Array} ids - @return {Promise} promise - */ - findMany: function(store, type, ids) { - var fixtures = this.fixturesForType(type); - - Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures); - - if (fixtures) { - fixtures = fixtures.filter(function(item) { - return indexOf(ids, item.id) !== -1; - }); - } - - if (fixtures) { - return this.simulateRemoteCall(function() { - return fixtures; - }, this); - } - }, - - /** - @private - @method findAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} sinceToken - @return {Promise} promise - */ - findAll: function(store, type) { - var fixtures = this.fixturesForType(type); - - Ember.assert("Unable to find fixtures for model type "+type.toString(), fixtures); - - return this.simulateRemoteCall(function() { - return fixtures; - }, this); - }, - - /** - @private - @method findQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} query - @param {DS.AdapterPopulatedRecordArray} recordArray - @return {Promise} promise - */ - findQuery: function(store, type, query, array) { - var fixtures = this.fixturesForType(type); - - Ember.assert("Unable to find fixtures for model type " + type.toString(), fixtures); - - fixtures = this.queryFixtures(fixtures, query, type); - - if (fixtures) { - return this.simulateRemoteCall(function() { - return fixtures; - }, this); - } - }, - - /** - @method createRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - createRecord: function(store, type, record) { - var fixture = this.mockJSON(store, type, record); - - this.updateFixtures(type, fixture); - - return this.simulateRemoteCall(function() { - return fixture; - }, this); - }, - - /** - @method updateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - updateRecord: function(store, type, record) { - var fixture = this.mockJSON(store, type, record); - - this.updateFixtures(type, fixture); - - return this.simulateRemoteCall(function() { - return fixture; - }, this); - }, - - /** - @method deleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - deleteRecord: function(store, type, record) { - var fixture = this.mockJSON(store, type, record); - - this.deleteLoadedFixture(type, fixture); - - return this.simulateRemoteCall(function() { - // no payload in a deletion - return null; - }); - }, - - /* - @method deleteLoadedFixture - @private - @param type - @param record - */ - deleteLoadedFixture: function(type, record) { - var existingFixture = this.findExistingFixture(type, record); - - if(existingFixture) { - var index = indexOf(type.FIXTURES, existingFixture); - type.FIXTURES.splice(index, 1); - return true; - } - }, - - /* - @method findExistingFixture - @private - @param type - @param record - */ - findExistingFixture: function(type, record) { - var fixtures = this.fixturesForType(type); - var id = get(record, 'id'); - - return this.findFixtureById(fixtures, id); - }, - - /* - @method findFixtureById - @private - @param fixtures - @param id - */ - findFixtureById: function(fixtures, id) { - return Ember.A(fixtures).find(function(r) { - if(''+get(r, 'id') === ''+id) { - return true; - } else { - return false; - } - }); - }, - - /* - @method simulateRemoteCall - @private - @param callback - @param context - */ - simulateRemoteCall: function(callback, context) { - var adapter = this; - - return new Ember.RSVP.Promise(function(resolve) { - if (get(adapter, 'simulateRemoteResponse')) { - // Schedule with setTimeout - Ember.run.later(function() { - resolve(callback.call(context)); - }, get(adapter, 'latency')); - } else { - // Asynchronous, but at the of the runloop with zero latency - Ember.run.schedule('actions', null, function() { - resolve(callback.call(context)); - }); - } - }, "DS: FixtureAdapter#simulateRemoteCall"); - } - }); - }); -define("ember-data/lib/adapters/rest_adapter", - ["../system/adapter","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var Adapter = __dependency1__["default"]; - var get = Ember.get; - var forEach = Ember.ArrayPolyfills.forEach; - - /** - The REST adapter allows your store to communicate with an HTTP server by - transmitting JSON via XHR. Most Ember.js apps that consume a JSON API - should use the REST adapter. - - This adapter is designed around the idea that the JSON exchanged with - the server should be conventional. - - ## JSON Structure - - The REST adapter expects the JSON returned from your server to follow - these conventions. - - ### Object Root - - The JSON payload should be an object that contains the record inside a - root property. For example, in response to a `GET` request for - `/posts/1`, the JSON should look like this: - - ```js - { - "post": { - "title": "I'm Running to Reform the W3C's Tag", - "author": "Yehuda Katz" - } - } - ``` - - ### Conventional Names - - Attribute names in your JSON payload should be the camelCased versions of - the attributes in your Ember.js models. - - For example, if you have a `Person` model: - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string') - }); - ``` - - The JSON returned should look like this: - - ```js - { - "person": { - "firstName": "Barack", - "lastName": "Obama", - "occupation": "President" - } - } - ``` - - ## Customization - - ### Endpoint path customization - - Endpoint paths can be prefixed with a `namespace` by setting the namespace - property on the adapter: - - ```js - DS.RESTAdapter.reopen({ - namespace: 'api/1' - }); - ``` - Requests for `App.Person` would now target `/api/1/people/1`. - - ### Host customization - - An adapter can target other hosts by setting the `host` property. - - ```js - DS.RESTAdapter.reopen({ - host: 'https://api.example.com' - }); - ``` - - ### Headers customization - - Some APIs require HTTP headers, e.g. to provide an API key. Arbitrary - headers can be set as key/value pairs on the `RESTAdapter`'s `headers` - object and Ember Data will send them along with each ajax request. - - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: { - "API_KEY": "secret key", - "ANOTHER_HEADER": "Some header value" - } - }); - ``` - - `headers` can also be used as a computed property to support dynamic - headers. In the example below, the `session` object has been - injected into an adapter by Ember's container. - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: function() { - return { - "API_KEY": this.get("session.authToken"), - "ANOTHER_HEADER": "Some header value" - }; - }.property("session.authToken") - }); - ``` - - In some cases, your dynamic headers may require data from some - object outside of Ember's observer system (for example - `document.cookie`). You can use the - [volatile](/api/classes/Ember.ComputedProperty.html#method_volatile) - function to set the property into a non-cached mode causing the headers to - be recomputed with every request. - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: function() { - return { - "API_KEY": Ember.get(document.cookie.match(/apiKey\=([^;]*)/), "1"), - "ANOTHER_HEADER": "Some header value" - }; - }.property().volatile() - }); - ``` - - @class RESTAdapter - @constructor - @namespace DS - @extends DS.Adapter - */ - __exports__["default"] = Adapter.extend({ - defaultSerializer: '-rest', - /** - Endpoint paths can be prefixed with a `namespace` by setting the namespace - property on the adapter: - - ```javascript - DS.RESTAdapter.reopen({ - namespace: 'api/1' - }); - ``` - - Requests for `App.Post` would now target `/api/1/post/`. - - @property namespace - @type {String} - */ - - /** - An adapter can target other hosts by setting the `host` property. - - ```javascript - DS.RESTAdapter.reopen({ - host: 'https://api.example.com' - }); - ``` - - Requests for `App.Post` would now target `https://api.example.com/post/`. - - @property host - @type {String} - */ - - /** - Some APIs require HTTP headers, e.g. to provide an API - key. Arbitrary headers can be set as key/value pairs on the - `RESTAdapter`'s `headers` object and Ember Data will send them - along with each ajax request. For dynamic headers see [headers - customization](/api/data/classes/DS.RESTAdapter.html#toc_headers-customization). - - ```javascript - App.ApplicationAdapter = DS.RESTAdapter.extend({ - headers: { - "API_KEY": "secret key", - "ANOTHER_HEADER": "Some header value" - } - }); - ``` - - @property headers - @type {Object} - */ - - /** - Called by the store in order to fetch the JSON for a given - type and ID. - - The `find` method makes an Ajax request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - This method performs an HTTP `GET` request with the id provided as part of the query string. - - @method find - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} id - @return {Promise} promise - */ - find: function(store, type, id) { - return this.ajax(this.buildURL(type.typeKey, id), 'GET'); - }, - - /** - Called by the store in order to fetch a JSON array for all - of the records for a given type. - - The `findAll` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - @private - @method findAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {String} sinceToken - @return {Promise} promise - */ - findAll: function(store, type, sinceToken) { - var query; - - if (sinceToken) { - query = { since: sinceToken }; - } - - return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); - }, - - /** - Called by the store in order to fetch a JSON array for - the records that match a particular query. - - The `findQuery` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - The `query` argument is a simple JavaScript object that will be passed directly - to the server as parameters. - - @private - @method findQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} query - @return {Promise} promise - */ - findQuery: function(store, type, query) { - return this.ajax(this.buildURL(type.typeKey), 'GET', { data: query }); - }, - - /** - Called by the store in order to fetch a JSON array for - the unloaded records in a has-many relationship that were originally - specified as IDs. - - For example, if the original payload looks like: - - ```js - { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2, 3 ] - } - ``` - - The IDs will be passed as a URL-encoded Array of IDs, in this form: - - ``` - ids[]=1&ids[]=2&ids[]=3 - ``` - - Many servers, such as Rails and PHP, will automatically convert this URL-encoded array - into an Array for you on the server-side. If you want to encode the - IDs, differently, just override this (one-line) method. - - The `findMany` method makes an Ajax (HTTP GET) request to a URL computed by `buildURL`, and returns a - promise for the resulting payload. - - @method findMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Array} ids - @return {Promise} promise - */ - findMany: function(store, type, ids) { - return this.ajax(this.buildURL(type.typeKey), 'GET', { data: { ids: ids } }); - }, - - /** - Called by the store in order to fetch a JSON array for - the unloaded records in a has-many relationship that were originally - specified as a URL (inside of `links`). - - For example, if your original payload looks like this: - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "links": { "comments": "/posts/1/comments" } - } - } - ``` - - This method will be called with the parent record and `/posts/1/comments`. - - The `findHasMany` method will make an Ajax (HTTP GET) request to the originally specified URL. - If the URL is host-relative (starting with a single slash), the - request will use the host specified on the adapter (if any). - - @method findHasMany - @param {DS.Store} store - @param {DS.Model} record - @param {String} url - @return {Promise} promise - */ - findHasMany: function(store, record, url) { - var host = get(this, 'host'), - id = get(record, 'id'), - type = record.constructor.typeKey; - - if (host && url.charAt(0) === '/' && url.charAt(1) !== '/') { - url = host + url; - } - - return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET'); - }, - - /** - Called by the store in order to fetch a JSON array for - the unloaded records in a belongs-to relationship that were originally - specified as a URL (inside of `links`). - - For example, if your original payload looks like this: - - ```js - { - "person": { - "id": 1, - "name": "Tom Dale", - "links": { "group": "/people/1/group" } - } - } - ``` - - This method will be called with the parent record and `/people/1/group`. - - The `findBelongsTo` method will make an Ajax (HTTP GET) request to the originally specified URL. - - @method findBelongsTo - @param {DS.Store} store - @param {DS.Model} record - @param {String} url - @return {Promise} promise - */ - findBelongsTo: function(store, record, url) { - var id = get(record, 'id'), - type = record.constructor.typeKey; - - return this.ajax(this.urlPrefix(url, this.buildURL(type, id)), 'GET'); - }, - - /** - Called by the store when a newly created record is - saved via the `save` method on a model record instance. - - The `createRecord` method serializes the record and makes an Ajax (HTTP POST) request - to a URL computed by `buildURL`. - - See `serialize` for information on how to customize the serialized form - of a record. - - @method createRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - createRecord: function(store, type, record) { - var data = {}; - var serializer = store.serializerFor(type.typeKey); - - serializer.serializeIntoHash(data, type, record, { includeId: true }); - - return this.ajax(this.buildURL(type.typeKey), "POST", { data: data }); - }, - - /** - Called by the store when an existing record is saved - via the `save` method on a model record instance. - - The `updateRecord` method serializes the record and makes an Ajax (HTTP PUT) request - to a URL computed by `buildURL`. - - See `serialize` for information on how to customize the serialized form - of a record. - - @method updateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - updateRecord: function(store, type, record) { - var data = {}; - var serializer = store.serializerFor(type.typeKey); - - serializer.serializeIntoHash(data, type, record); - - var id = get(record, 'id'); - - return this.ajax(this.buildURL(type.typeKey, id), "PUT", { data: data }); - }, - - /** - Called by the store when a record is deleted. - - The `deleteRecord` method makes an Ajax (HTTP DELETE) request to a URL computed by `buildURL`. - - @method deleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {DS.Model} record - @return {Promise} promise - */ - deleteRecord: function(store, type, record) { - var id = get(record, 'id'); - - return this.ajax(this.buildURL(type.typeKey, id), "DELETE"); - }, - - /** - Builds a URL for a given type and optional ID. - - By default, it pluralizes the type's name (for example, 'post' - becomes 'posts' and 'person' becomes 'people'). To override the - pluralization see [pathForType](#method_pathForType). - - If an ID is specified, it adds the ID to the path generated - for the type, separated by a `/`. - - @method buildURL - @param {String} type - @param {String} id - @return {String} url - */ - buildURL: function(type, id) { - var url = [], - host = get(this, 'host'), - prefix = this.urlPrefix(); - - if (type) { url.push(this.pathForType(type)); } - if (id) { url.push(id); } - - if (prefix) { url.unshift(prefix); } - - url = url.join('/'); - if (!host && url) { url = '/' + url; } - - return url; - }, - - /** - @method urlPrefix - @private - @param {String} path - @param {String} parentUrl - @return {String} urlPrefix - */ - urlPrefix: function(path, parentURL) { - var host = get(this, 'host'), - namespace = get(this, 'namespace'), - url = []; - - if (path) { - // Absolute path - if (path.charAt(0) === '/') { - if (host) { - path = path.slice(1); - url.push(host); - } - // Relative path - } else if (!/^http(s)?:\/\//.test(path)) { - url.push(parentURL); - } - } else { - if (host) { url.push(host); } - if (namespace) { url.push(namespace); } - } - - if (path) { - url.push(path); - } - - return url.join('/'); - }, - - /** - Determines the pathname for a given type. - - By default, it pluralizes the type's name (for example, - 'post' becomes 'posts' and 'person' becomes 'people'). - - ### Pathname customization - - For example if you have an object LineItem with an - endpoint of "/line_items/". - - ```js - App.ApplicationAdapter = DS.RESTAdapter.extend({ - pathForType: function(type) { - var decamelized = Ember.String.decamelize(type); - return Ember.String.pluralize(decamelized); - } - }); - ``` - - @method pathForType - @param {String} type - @return {String} path - **/ - pathForType: function(type) { - var camelized = Ember.String.camelize(type); - return Ember.String.pluralize(camelized); - }, - - /** - Takes an ajax response, and returns a relevant error. - - Returning a `DS.InvalidError` from this method will cause the - record to transition into the `invalid` state and make the - `errors` object available on the record. - - ```javascript - App.ApplicationAdapter = DS.RESTAdapter.extend({ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); - - if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"]; - - return new DS.InvalidError(jsonErrors); - } else { - return error; - } - } - }); - ``` - - Note: As a correctness optimization, the default implementation of - the `ajaxError` method strips out the `then` method from jquery's - ajax response (jqXHR). This is important because the jqXHR's - `then` method fulfills the promise with itself resulting in a - circular "thenable" chain which may cause problems for some - promise libraries. - - @method ajaxError - @param {Object} jqXHR - @return {Object} jqXHR - */ - ajaxError: function(jqXHR) { - if (jqXHR && typeof jqXHR === 'object') { - jqXHR.then = null; - } - - return jqXHR; - }, - - /** - Takes a URL, an HTTP method and a hash of data, and makes an - HTTP request. - - When the server responds with a payload, Ember Data will call into `extractSingle` - or `extractArray` (depending on whether the original query was for one record or - many records). - - By default, `ajax` method has the following behavior: - - * It sets the response `dataType` to `"json"` - * If the HTTP method is not `"GET"`, it sets the `Content-Type` to be - `application/json; charset=utf-8` - * If the HTTP method is not `"GET"`, it stringifies the data passed in. The - data is the serialized record in the case of a save. - * Registers success and failure handlers. - - @method ajax - @private - @param {String} url - @param {String} type The request type GET, POST, PUT, DELETE etc. - @param {Object} hash - @return {Promise} promise - */ - ajax: function(url, type, hash) { - var adapter = this; - - return new Ember.RSVP.Promise(function(resolve, reject) { - hash = adapter.ajaxOptions(url, type, hash); - - hash.success = function(json) { - Ember.run(null, resolve, json); - }; - - hash.error = function(jqXHR, textStatus, errorThrown) { - Ember.run(null, reject, adapter.ajaxError(jqXHR)); - }; - - Ember.$.ajax(hash); - }, "DS: RESTAdapter#ajax " + type + " to " + url); - }, - - /** - @method ajaxOptions - @private - @param {String} url - @param {String} type The request type GET, POST, PUT, DELETE etc. - @param {Object} hash - @return {Object} hash - */ - ajaxOptions: function(url, type, hash) { - hash = hash || {}; - hash.url = url; - hash.type = type; - hash.dataType = 'json'; - hash.context = this; - - if (hash.data && type !== 'GET') { - hash.contentType = 'application/json; charset=utf-8'; - hash.data = JSON.stringify(hash.data); - } - - var headers = get(this, 'headers'); - if (headers !== undefined) { - hash.beforeSend = function (xhr) { - forEach.call(Ember.keys(headers), function(key) { - xhr.setRequestHeader(key, headers[key]); - }); - }; - } - - - return hash; - } - - }); - }); -define("ember-data/lib/core", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember-data - */ - - /** - All Ember Data methods and functions are defined inside of this namespace. - - @class DS - @static - */ - var DS; - if ('undefined' === typeof DS) { - /** - @property VERSION - @type String - @default '1.0.0-beta.9+canary.9a5cc255' - @static - */ - DS = Ember.Namespace.create({ - VERSION: '1.0.0-beta.9+canary.9a5cc255' - }); - - if (Ember.libraries) { - Ember.libraries.registerCoreLibrary('Ember Data', DS.VERSION); - } - } - - __exports__["default"] = DS; - }); -define("ember-data/lib/ember-initializer", - ["./setup-container"], - function(__dependency1__) { - "use strict"; - var setupContainer = __dependency1__["default"]; - - var K = Ember.K; - - /** - @module ember-data - */ - - /** - - This code initializes Ember-Data onto an Ember application. - - If an Ember.js developer defines a subclass of DS.Store on their application, - as `App.ApplicationStore` (or via a module system that resolves to `store:application`) - this code will automatically instantiate it and make it available on the - router. - - Additionally, after an application's controllers have been injected, they will - each have the store made available to them. - - For example, imagine an Ember.js application with the following classes: - - App.ApplicationStore = DS.Store.extend({ - adapter: 'custom' - }); - - App.PostsController = Ember.ArrayController.extend({ - // ... - }); - - When the application is initialized, `App.ApplicationStore` will automatically be - instantiated, and the instance of `App.PostsController` will have its `store` - property set to that instance. - - Note that this code will only be run if the `ember-application` package is - loaded. If Ember Data is being used in an environment other than a - typical application (e.g., node.js where only `ember-runtime` is available), - this code will be ignored. - */ - - Ember.onLoad('Ember.Application', function(Application) { - - Application.initializer({ - name: "ember-data", - initialize: setupContainer - }); - - // Deprecated initializers to satisfy old code that depended on them - - Application.initializer({ - name: "store", - after: "ember-data", - initialize: K - }); - - Application.initializer({ - name: "activeModelAdapter", - before: "store", - initialize: K - }); - - Application.initializer({ - name: "transforms", - before: "store", - initialize: K - }); - - Application.initializer({ - name: "data-adapter", - before: "store", - initialize: K - }); - - Application.initializer({ - name: "injectStore", - before: "store", - initialize: K - }); - }); - }); -define("ember-data/lib/ext/date", - [], - function() { - "use strict"; - /** - @module ember-data - */ - - /** - Date.parse with progressive enhancement for ISO 8601 - - © 2011 Colin Snover - - Released under MIT license. - - @class Date - @namespace Ember - @static - */ - Ember.Date = Ember.Date || {}; - - var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; - - /** - @method parse - @param date - */ - Ember.Date.parse = function (date) { - var timestamp, struct, minutesOffset = 0; - - // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string - // before falling back to any implementation-specific date parsing, so that’s what we do, even if native - // implementations could be faster - // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm - if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) { - // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC - for (var i = 0, k; (k = numericKeys[i]); ++i) { - struct[k] = +struct[k] || 0; - } - - // allow undefined days and months - struct[2] = (+struct[2] || 1) - 1; - struct[3] = +struct[3] || 1; - - if (struct[8] !== 'Z' && struct[9] !== undefined) { - minutesOffset = struct[10] * 60 + struct[11]; - - if (struct[9] === '+') { - minutesOffset = 0 - minutesOffset; - } - } - - timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); - } - else { - timestamp = origParse ? origParse(date) : NaN; - } - - return timestamp; - }; - - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) { - Date.parse = Ember.Date.parse; - } - }); -define("ember-data/lib/initializers/data_adapter", - ["../system/debug/debug_adapter","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var DebugAdapter = __dependency1__["default"]; - - /** - Configures a container with injections on Ember applications - for the Ember-Data store. Accepts an optional namespace argument. - - @method initializeStoreInjections - @param {Ember.Container} container - */ - __exports__["default"] = function initializeDebugAdapter(container){ - container.register('data-adapter:main', DebugAdapter); - }; - }); -define("ember-data/lib/initializers/store", - ["../serializers","../adapters","../system/container_proxy","../system/store","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var JSONSerializer = __dependency1__.JSONSerializer; - var RESTSerializer = __dependency1__.RESTSerializer; - var RESTAdapter = __dependency2__.RESTAdapter; - var ContainerProxy = __dependency3__["default"]; - var Store = __dependency4__["default"]; - - /** - Configures a container for use with an Ember-Data - store. Accepts an optional namespace argument. - - @method initializeStore - @param {Ember.Container} container - @param {Object} [application] an application namespace - */ - __exports__["default"] = function initializeStore(container, application){ - Ember.deprecate('Specifying a custom Store for Ember Data on your global namespace as `App.Store` ' + - 'has been deprecated. Please use `App.ApplicationStore` instead.', !(application && application.Store)); - - container.register('store:main', container.lookupFactory('store:application') || (application && application.Store) || Store); - - // allow older names to be looked up - - var proxy = new ContainerProxy(container); - proxy.registerDeprecations([ - {deprecated: 'serializer:_default', valid: 'serializer:-default'}, - {deprecated: 'serializer:_rest', valid: 'serializer:-rest'}, - {deprecated: 'adapter:_rest', valid: 'adapter:-rest'} - ]); - - // new go forward paths - container.register('serializer:-default', JSONSerializer); - container.register('serializer:-rest', RESTSerializer); - container.register('adapter:-rest', RESTAdapter); - - // Eagerly generate the store so defaultStore is populated. - // TODO: Do this in a finisher hook - container.lookup('store:main'); - }; - }); -define("ember-data/lib/initializers/store_injections", - ["exports"], - function(__exports__) { - "use strict"; - /** - Configures a container with injections on Ember applications - for the Ember-Data store. Accepts an optional namespace argument. - - @method initializeStoreInjections - @param {Ember.Container} container - */ - __exports__["default"] = function initializeStoreInjections(container){ - container.injection('controller', 'store', 'store:main'); - container.injection('route', 'store', 'store:main'); - container.injection('serializer', 'store', 'store:main'); - container.injection('data-adapter', 'store', 'store:main'); - }; - }); -define("ember-data/lib/initializers/transforms", - ["../transforms","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var BooleanTransform = __dependency1__.BooleanTransform; - var DateTransform = __dependency1__.DateTransform; - var StringTransform = __dependency1__.StringTransform; - var NumberTransform = __dependency1__.NumberTransform; - - /** - Configures a container for use with Ember-Data - transforms. - - @method initializeTransforms - @param {Ember.Container} container - */ - __exports__["default"] = function initializeTransforms(container){ - container.register('transform:boolean', BooleanTransform); - container.register('transform:date', DateTransform); - container.register('transform:number', NumberTransform); - container.register('transform:string', StringTransform); - }; - }); -define("ember-data/lib/main", - ["./core","./ext/date","./system/store","./system/model","./system/changes","./system/adapter","./system/debug","./system/record_arrays","./system/record_array_manager","./adapters","./serializers/json_serializer","./serializers/rest_serializer","../../ember-inflector/lib/main","../../activemodel-adapter/lib/main","./transforms","./system/relationships","./ember-initializer","./setup-container","./system/container_proxy","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __exports__) { - "use strict"; - /** - Ember Data - - @module ember-data - @main ember-data - */ - - // support RSVP 2.x via resolve, but prefer RSVP 3.x's Promise.cast - Ember.RSVP.Promise.cast = Ember.RSVP.Promise.cast || Ember.RSVP.resolve; - - var DS = __dependency1__["default"]; - - var Store = __dependency3__.Store; - var PromiseArray = __dependency3__.PromiseArray; - var PromiseObject = __dependency3__.PromiseObject; - var Model = __dependency4__.Model; - var Errors = __dependency4__.Errors; - var RootState = __dependency4__.RootState; - var attr = __dependency4__.attr; - var AttributeChange = __dependency5__.AttributeChange; - var RelationshipChange = __dependency5__.RelationshipChange; - var RelationshipChangeAdd = __dependency5__.RelationshipChangeAdd; - var RelationshipChangeRemove = __dependency5__.RelationshipChangeRemove; - var OneToManyChange = __dependency5__.OneToManyChange; - var ManyToNoneChange = __dependency5__.ManyToNoneChange; - var OneToOneChange = __dependency5__.OneToOneChange; - var ManyToManyChange = __dependency5__.ManyToManyChange; - var InvalidError = __dependency6__.InvalidError; - var Adapter = __dependency6__.Adapter; - var DebugAdapter = __dependency7__["default"]; - var RecordArray = __dependency8__.RecordArray; - var FilteredRecordArray = __dependency8__.FilteredRecordArray; - var AdapterPopulatedRecordArray = __dependency8__.AdapterPopulatedRecordArray; - var ManyArray = __dependency8__.ManyArray; - var RecordArrayManager = __dependency9__["default"]; - var RESTAdapter = __dependency10__.RESTAdapter; - var FixtureAdapter = __dependency10__.FixtureAdapter; - var JSONSerializer = __dependency11__["default"]; - var RESTSerializer = __dependency12__["default"]; - var ActiveModelAdapter = __dependency14__.ActiveModelAdapter; - var ActiveModelSerializer = __dependency14__.ActiveModelSerializer; - var EmbeddedRecordsMixin = __dependency14__.EmbeddedRecordsMixin; - - var Transform = __dependency15__.Transform; - var DateTransform = __dependency15__.DateTransform; - var NumberTransform = __dependency15__.NumberTransform; - var StringTransform = __dependency15__.StringTransform; - var BooleanTransform = __dependency15__.BooleanTransform; - - var hasMany = __dependency16__.hasMany; - var belongsTo = __dependency16__.belongsTo; - var setupContainer = __dependency18__["default"]; - - var ContainerProxy = __dependency19__["default"]; - - DS.Store = Store; - DS.PromiseArray = PromiseArray; - DS.PromiseObject = PromiseObject; - - DS.Model = Model; - DS.RootState = RootState; - DS.attr = attr; - DS.Errors = Errors; - - DS.AttributeChange = AttributeChange; - DS.RelationshipChange = RelationshipChange; - DS.RelationshipChangeAdd = RelationshipChangeAdd; - DS.OneToManyChange = OneToManyChange; - DS.ManyToNoneChange = OneToManyChange; - DS.OneToOneChange = OneToOneChange; - DS.ManyToManyChange = ManyToManyChange; - - DS.Adapter = Adapter; - DS.InvalidError = InvalidError; - - DS.DebugAdapter = DebugAdapter; - - DS.RecordArray = RecordArray; - DS.FilteredRecordArray = FilteredRecordArray; - DS.AdapterPopulatedRecordArray = AdapterPopulatedRecordArray; - DS.ManyArray = ManyArray; - - DS.RecordArrayManager = RecordArrayManager; - - DS.RESTAdapter = RESTAdapter; - DS.FixtureAdapter = FixtureAdapter; - - DS.RESTSerializer = RESTSerializer; - DS.JSONSerializer = JSONSerializer; - - DS.Transform = Transform; - DS.DateTransform = DateTransform; - DS.StringTransform = StringTransform; - DS.NumberTransform = NumberTransform; - DS.BooleanTransform = BooleanTransform; - - DS.ActiveModelAdapter = ActiveModelAdapter; - DS.ActiveModelSerializer = ActiveModelSerializer; - DS.EmbeddedRecordsMixin = EmbeddedRecordsMixin; - - DS.belongsTo = belongsTo; - DS.hasMany = hasMany; - - DS.ContainerProxy = ContainerProxy; - - DS._setupContainer = setupContainer; - - Ember.lookup.DS = DS; - - __exports__["default"] = DS; - }); -define("ember-data/lib/serializers", - ["./serializers/json_serializer","./serializers/rest_serializer","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var JSONSerializer = __dependency1__["default"]; - var RESTSerializer = __dependency2__["default"]; - - __exports__.JSONSerializer = JSONSerializer; - __exports__.RESTSerializer = RESTSerializer; - }); -define("ember-data/lib/serializers/json_serializer", - ["../system/changes","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var RelationshipChange = __dependency1__.RelationshipChange; - var get = Ember.get; - var set = Ember.set; - var isNone = Ember.isNone; - var map = Ember.ArrayPolyfills.map; - - /** - In Ember Data a Serializer is used to serialize and deserialize - records when they are transferred in and out of an external source. - This process involves normalizing property names, transforming - attribute values and serializing relationships. - - For maximum performance Ember Data recommends you use the - [RESTSerializer](DS.RESTSerializer.html) or one of its subclasses. - - `JSONSerializer` is useful for simpler or legacy backends that may - not support the http://jsonapi.org/ spec. - - @class JSONSerializer - @namespace DS - */ - __exports__["default"] = Ember.Object.extend({ - /** - The primaryKey is used when serializing and deserializing - data. Ember Data always uses the `id` property to store the id of - the record. The external source may not always follow this - convention. In these cases it is useful to override the - primaryKey property to match the primaryKey of your external - store. - - Example - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - primaryKey: '_id' - }); - ``` - - @property primaryKey - @type {String} - @default 'id' - */ - primaryKey: 'id', - - /** - The `attrs` object can be used to declare a simple mapping between - property names on `DS.Model` records and payload keys in the - serialized JSON object representing the record. An object with the - property `key` can also be used to designate the attribute's key on - the response payload. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string'), - occupation: DS.attr('string'), - admin: DS.attr('boolean') - }); - - App.PersonSerializer = DS.JSONSerializer.extend({ - attrs: { - admin: 'is_admin', - occupation: {key: 'career'} - } - }); - ``` - - @property attrs - @type {Object} - */ - - /** - Given a subclass of `DS.Model` and a JSON object this method will - iterate through each attribute of the `DS.Model` and invoke the - `DS.Transform#deserialize` method on the matching property of the - JSON object. This method is typically called after the - serializer's `normalize` method. - - @method applyTransforms - @private - @param {subclass of DS.Model} type - @param {Object} data The data to transform - @return {Object} data The transformed data object - */ - applyTransforms: function(type, data) { - type.eachTransformedAttribute(function(key, type) { - var transform = this.transformFor(type); - data[key] = transform.deserialize(data[key]); - }, this); - - return data; - }, - - /** - Normalizes a part of the JSON payload returned by - the server. You should override this method, munge the hash - and call super if you have generic normalization to do. - - It takes the type of the record that is being normalized - (as a DS.Model class), the property where the hash was - originally found, and the hash to normalize. - - You can use this method, for example, to normalize underscored keys to camelized - or other general-purpose normalizations. - - Example - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - normalize: function(type, hash) { - var fields = Ember.get(type, 'fields'); - fields.forEach(function(field) { - var payloadField = Ember.String.underscore(field); - if (field === payloadField) { return; } - - hash[field] = hash[payloadField]; - delete hash[payloadField]; - }); - return this._super.apply(this, arguments); - } - }); - ``` - - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @return {Object} - */ - normalize: function(type, hash) { - if (!hash) { return hash; } - - this.normalizeId(hash); - this.normalizeUsingDeclaredMapping(type, hash); - this.applyTransforms(type, hash); - return hash; - }, - - /** - @method normalizeUsingDeclaredMapping - @private - */ - normalizeUsingDeclaredMapping: function(type, hash) { - var attrs = get(this, 'attrs'), payloadKey, key; - - if (attrs) { - for (key in attrs) { - payloadKey = attrs[key]; - if (payloadKey && payloadKey.key) { - payloadKey = payloadKey.key; - } - if (typeof payloadKey === 'string') { - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - } - } - } - }, - /** - @method normalizeId - @private - */ - normalizeId: function(hash) { - var primaryKey = get(this, 'primaryKey'); - - if (primaryKey === 'id') { return; } - - hash.id = hash[primaryKey]; - delete hash[primaryKey]; - }, - - // SERIALIZE - /** - Called when a record is saved in order to convert the - record into JSON. - - By default, it creates a JSON object with a key for - each attribute and belongsTo relationship. - - For example, consider this model: - - ```javascript - App.Comment = DS.Model.extend({ - title: DS.attr(), - body: DS.attr(), - - author: DS.belongsTo('user') - }); - ``` - - The default serialization would create a JSON object like: - - ```javascript - { - "title": "Rails is unagi", - "body": "Rails? Omakase? O_O", - "author": 12 - } - ``` - - By default, attributes are passed through as-is, unless - you specified an attribute type (`DS.attr('date')`). If - you specify a transform, the JavaScript value will be - serialized when inserted into the JSON hash. - - By default, belongs-to relationships are converted into - IDs when inserted into the JSON hash. - - ## IDs - - `serialize` takes an options hash with a single option: - `includeId`. If this option is `true`, `serialize` will, - by default include the ID in the JSON object it builds. - - The adapter passes in `includeId: true` when serializing - a record for `createRecord`, but not for `updateRecord`. - - ## Customization - - Your server may expect a different JSON format than the - built-in serialization format. - - In that case, you can implement `serialize` yourself and - return a JSON hash of your choosing. - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serialize: function(post, options) { - var json = { - POST_TTL: post.get('title'), - POST_BDY: post.get('body'), - POST_CMS: post.get('comments').mapBy('id') - } - - if (options.includeId) { - json.POST_ID_ = post.get('id'); - } - - return json; - } - }); - ``` - - ## Customizing an App-Wide Serializer - - If you want to define a serializer for your entire - application, you'll probably want to use `eachAttribute` - and `eachRelationship` on the record. - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - serialize: function(record, options) { - var json = {}; - - record.eachAttribute(function(name) { - json[serverAttributeName(name)] = record.get(name); - }) - - record.eachRelationship(function(name, relationship) { - if (relationship.kind === 'hasMany') { - json[serverHasManyName(name)] = record.get(name).mapBy('id'); - } - }); - - if (options.includeId) { - json.ID_ = record.get('id'); - } - - return json; - } - }); - - function serverAttributeName(attribute) { - return attribute.underscore().toUpperCase(); - } - - function serverHasManyName(name) { - return serverAttributeName(name.singularize()) + "_IDS"; - } - ``` - - This serializer will generate JSON that looks like this: - - ```javascript - { - "TITLE": "Rails is omakase", - "BODY": "Yep. Omakase.", - "COMMENT_IDS": [ 1, 2, 3 ] - } - ``` - - ## Tweaking the Default JSON - - If you just want to do some small tweaks on the default JSON, - you can call super first and make the tweaks on the returned - JSON. - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serialize: function(record, options) { - var json = this._super.apply(this, arguments); - - json.subject = json.title; - delete json.title; - - return json; - } - }); - ``` - - @method serialize - @param {subclass of DS.Model} record - @param {Object} options - @return {Object} json - */ - serialize: function(record, options) { - var json = {}; - - if (options && options.includeId) { - var id = get(record, 'id'); - - if (id) { - json[get(this, 'primaryKey')] = id; - } - } - - record.eachAttribute(function(key, attribute) { - this.serializeAttribute(record, json, key, attribute); - }, this); - - record.eachRelationship(function(key, relationship) { - if (relationship.kind === 'belongsTo') { - this.serializeBelongsTo(record, json, relationship); - } else if (relationship.kind === 'hasMany') { - this.serializeHasMany(record, json, relationship); - } - }, this); - - return json; - }, - - /** - `serializeAttribute` can be used to customize how `DS.attr` - properties are serialized - - For example if you wanted to ensure all your attributes were always - serialized as properties on an `attributes` object you could - write: - - ```javascript - App.ApplicationSerializer = DS.JSONSerializer.extend({ - serializeAttribute: function(record, json, key, attributes) { - json.attributes = json.attributes || {}; - this._super(record, json.attributes, key, attributes); - } - }); - ``` - - @method serializeAttribute - @param {DS.Model} record - @param {Object} json - @param {String} key - @param {Object} attribute - */ - serializeAttribute: function(record, json, key, attribute) { - var attrs = get(this, 'attrs'); - var value = get(record, key); - var type = attribute.type; - - if (type) { - var transform = this.transformFor(type); - value = transform.serialize(value); - } - - // if provided, use the mapping provided by `attrs` in - // the serializer - key = attrs && attrs[key] || (this.keyForAttribute ? this.keyForAttribute(key) : key); - - json[key] = value; - }, - - /** - `serializeBelongsTo` can be used to customize how `DS.belongsTo` - properties are serialized. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serializeBelongsTo: function(record, json, relationship) { - var key = relationship.key; - - var belongsTo = get(record, key); - - key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; - - json[key] = Ember.isNone(belongsTo) ? belongsTo : belongsTo.toJSON(); - } - }); - ``` - - @method serializeBelongsTo - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeBelongsTo: function(record, json, relationship) { - var key = relationship.key; - var belongsTo = get(record, key); - - key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo") : key; - - if (isNone(belongsTo)) { - json[key] = belongsTo; - } else { - json[key] = get(belongsTo, 'id'); - } - - if (relationship.options.polymorphic) { - this.serializePolymorphicType(record, json, relationship); - } - }, - - /** - `serializeHasMany` can be used to customize how `DS.hasMany` - properties are serialized. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - serializeHasMany: function(record, json, relationship) { - var key = relationship.key; - if (key === 'comments') { - return; - } else { - this._super.apply(this, arguments); - } - } - }); - ``` - - @method serializeHasMany - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializeHasMany: function(record, json, relationship) { - var key = relationship.key; - var payloadKey = this.keyForRelationship ? this.keyForRelationship(key, "hasMany") : key; - var relationshipType = RelationshipChange.determineRelationshipType(record.constructor, relationship); - - if (relationshipType === 'manyToNone' || relationshipType === 'manyToMany') { - json[payloadKey] = get(record, key).mapBy('id'); - // TODO support for polymorphic manyToNone and manyToMany relationships - } - }, - - /** - You can use this method to customize how polymorphic objects are - serialized. Objects are considered to be polymorphic if - `{polymorphic: true}` is pass as the second argument to the - `DS.belongsTo` function. - - Example - - ```javascript - App.CommentSerializer = DS.JSONSerializer.extend({ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key, - belongsTo = get(record, key); - key = this.keyForAttribute ? this.keyForAttribute(key) : key; - json[key + "_type"] = belongsTo.constructor.typeKey; - } - }); - ``` - - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializePolymorphicType: Ember.K, - - // EXTRACT - - /** - The `extract` method is used to deserialize payload data from the - server. By default the `JSONSerializer` does not push the records - into the store. However records that subclass `JSONSerializer` - such as the `RESTSerializer` may push records into the store as - part of the extract call. - - This method delegates to a more specific extract method based on - the `requestType`. - - Example - - ```javascript - var get = Ember.get; - socket.on('message', function(message) { - var modelName = message.model; - var data = message.data; - var type = store.modelFor(modelName); - var serializer = store.serializerFor(type.typeKey); - var record = serializer.extract(store, type, data, get(data, 'id'), 'single'); - store.push(modelName, record); - }); - ``` - - @method extract - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @param {String or Number} id - @param {String} requestType - @return {Object} json The deserialized payload - */ - extract: function(store, type, payload, id, requestType) { - this.extractMeta(store, type, payload); - - var specificExtract = "extract" + requestType.charAt(0).toUpperCase() + requestType.substr(1); - return this[specificExtract](store, type, payload, id, requestType); - }, - - /** - `extractFindAll` is a hook into the extract method used when a - call is made to `DS.Store#findAll`. By default this method is an - alias for [extractArray](#method_extractArray). - - @method extractFindAll - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects - */ - extractFindAll: function(store, type, payload){ - return this.extractArray(store, type, payload); - }, - /** - `extractFindQuery` is a hook into the extract method used when a - call is made to `DS.Store#findQuery`. By default this method is an - alias for [extractArray](#method_extractArray). - - @method extractFindQuery - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects - */ - extractFindQuery: function(store, type, payload){ - return this.extractArray(store, type, payload); - }, - /** - `extractFindMany` is a hook into the extract method used when a - call is made to `DS.Store#findMany`. By default this method is - alias for [extractArray](#method_extractArray). - - @method extractFindMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects - */ - extractFindMany: function(store, type, payload){ - return this.extractArray(store, type, payload); - }, - /** - `extractFindHasMany` is a hook into the extract method used when a - call is made to `DS.Store#findHasMany`. By default this method is - alias for [extractArray](#method_extractArray). - - @method extractFindHasMany - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects - */ - extractFindHasMany: function(store, type, payload){ - return this.extractArray(store, type, payload); - }, - - /** - `extractCreateRecord` is a hook into the extract method used when a - call is made to `DS.Store#createRecord`. By default this method is - alias for [extractSave](#method_extractSave). - - @method extractCreateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractCreateRecord: function(store, type, payload) { - return this.extractSave(store, type, payload); - }, - /** - `extractUpdateRecord` is a hook into the extract method used when - a call is made to `DS.Store#update`. By default this method is alias - for [extractSave](#method_extractSave). - - @method extractUpdateRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractUpdateRecord: function(store, type, payload) { - return this.extractSave(store, type, payload); - }, - /** - `extractDeleteRecord` is a hook into the extract method used when - a call is made to `DS.Store#deleteRecord`. By default this method is - alias for [extractSave](#method_extractSave). - - @method extractDeleteRecord - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractDeleteRecord: function(store, type, payload) { - return this.extractSave(store, type, payload); - }, - - /** - `extractFind` is a hook into the extract method used when - a call is made to `DS.Store#find`. By default this method is - alias for [extractSingle](#method_extractSingle). - - @method extractFind - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractFind: function(store, type, payload) { - return this.extractSingle(store, type, payload); - }, - /** - `extractFindBelongsTo` is a hook into the extract method used when - a call is made to `DS.Store#findBelongsTo`. By default this method is - alias for [extractSingle](#method_extractSingle). - - @method extractFindBelongsTo - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractFindBelongsTo: function(store, type, payload) { - return this.extractSingle(store, type, payload); - }, - /** - `extractSave` is a hook into the extract method used when a call - is made to `DS.Model#save`. By default this method is alias - for [extractSingle](#method_extractSingle). - - @method extractSave - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractSave: function(store, type, payload) { - return this.extractSingle(store, type, payload); - }, - - /** - `extractSingle` is used to deserialize a single record returned - from the adapter. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractSingle: function(store, type, payload) { - payload.comments = payload._embedded.comment; - delete payload._embedded; - - return this._super(store, type, payload); - }, - }); - ``` - - @method extractSingle - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Object} json The deserialized payload - */ - extractSingle: function(store, type, payload) { - return this.normalize(type, payload); - }, - - /** - `extractArray` is used to deserialize an array of records - returned from the adapter. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractArray: function(store, type, payload) { - return payload.map(function(json) { - return this.extractSingle(store, type, json); - }, this); - } - }); - ``` - - @method extractArray - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - @return {Array} array An array of deserialized objects - */ - extractArray: function(store, type, arrayPayload) { - var serializer = this; - return map.call(arrayPayload, function(singlePayload) { - return serializer.normalize(type, singlePayload); - }); - }, - - /** - `extractMeta` is used to deserialize any meta information in the - adapter payload. By default Ember Data expects meta information to - be located on the `meta` property of the payload object. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - extractMeta: function(store, type, payload) { - if (payload && payload._pagination) { - store.metaForType(type, payload._pagination); - delete payload._pagination; - } - } - }); - ``` - - @method extractMeta - @param {DS.Store} store - @param {subclass of DS.Model} type - @param {Object} payload - */ - extractMeta: function(store, type, payload) { - if (payload && payload.meta) { - store.metaForType(type, payload.meta); - delete payload.meta; - } - }, - - /** - `keyForAttribute` can be used to define rules for how to convert an - attribute name in your model to a key in your JSON. - - Example - - ```javascript - App.ApplicationSerializer = DS.RESTSerializer.extend({ - keyForAttribute: function(attr) { - return Ember.String.underscore(attr).toUpperCase(); - } - }); - ``` - - @method keyForAttribute - @param {String} key - @return {String} normalized key - */ - - - /** - `keyForRelationship` can be used to define a custom key when - serializing relationship properties. By default `JSONSerializer` - does not provide an implementation of this method. - - Example - - ```javascript - App.PostSerializer = DS.JSONSerializer.extend({ - keyForRelationship: function(key, relationship) { - return 'rel_' + Ember.String.underscore(key); - } - }); - ``` - - @method keyForRelationship - @param {String} key - @param {String} relationship type - @return {String} normalized key - */ - - // HELPERS - - /** - @method transformFor - @private - @param {String} attributeType - @param {Boolean} skipAssertion - @return {DS.Transform} transform - */ - transformFor: function(attributeType, skipAssertion) { - var transform = this.container.lookup('transform:' + attributeType); - Ember.assert("Unable to find transform for '" + attributeType + "'", skipAssertion || !!transform); - return transform; - } - }); - }); -define("ember-data/lib/serializers/rest_serializer", - ["./json_serializer","ember-inflector/lib/system/string","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var JSONSerializer = __dependency1__["default"]; - var get = Ember.get; - var set = Ember.set; - var forEach = Ember.ArrayPolyfills.forEach; - var map = Ember.ArrayPolyfills.map; - var camelize = Ember.String.camelize; - - var singularize = __dependency2__.singularize; - - function coerceId(id) { - return id == null ? null : id + ''; - } - - /** - Normally, applications will use the `RESTSerializer` by implementing - the `normalize` method and individual normalizations under - `normalizeHash`. - - This allows you to do whatever kind of munging you need, and is - especially useful if your server is inconsistent and you need to - do munging differently for many different kinds of responses. - - See the `normalize` documentation for more information. - - ## Across the Board Normalization - - There are also a number of hooks that you might find useful to define - across-the-board rules for your payload. These rules will be useful - if your server is consistent, or if you're building an adapter for - an infrastructure service, like Parse, and want to encode service - conventions. - - For example, if all of your keys are underscored and all-caps, but - otherwise consistent with the names you use in your models, you - can implement across-the-board rules for how to convert an attribute - name in your model to a key in your JSON. - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - keyForAttribute: function(attr) { - return Ember.String.underscore(attr).toUpperCase(); - } - }); - ``` - - You can also implement `keyForRelationship`, which takes the name - of the relationship as the first parameter, and the kind of - relationship (`hasMany` or `belongsTo`) as the second parameter. - - @class RESTSerializer - @namespace DS - @extends DS.JSONSerializer - */ - __exports__["default"] = JSONSerializer.extend({ - /** - If you want to do normalizations specific to some part of the payload, you - can specify those under `normalizeHash`. - - For example, given the following json where the the `IDs` under - `"comments"` are provided as `_id` instead of `id`. - - ```javascript - { - "post": { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2 ] - }, - "comments": [{ - "_id": 1, - "body": "FIRST" - }, { - "_id": 2, - "body": "Rails is unagi" - }] - } - ``` - - You use `normalizeHash` to normalize just the comments: - - ```javascript - App.PostSerializer = DS.RESTSerializer.extend({ - normalizeHash: { - comments: function(hash) { - hash.id = hash._id; - delete hash._id; - return hash; - } - } - }); - ``` - - The key under `normalizeHash` is usually just the original key - that was in the original payload. However, key names will be - impacted by any modifications done in the `normalizePayload` - method. The `DS.RESTSerializer`'s default implementation makes no - changes to the payload keys. - - @property normalizeHash - @type {Object} - @default undefined - */ - - /** - Normalizes a part of the JSON payload returned by - the server. You should override this method, munge the hash - and call super if you have generic normalization to do. - - It takes the type of the record that is being normalized - (as a DS.Model class), the property where the hash was - originally found, and the hash to normalize. - - For example, if you have a payload that looks like this: - - ```js - { - "post": { - "id": 1, - "title": "Rails is omakase", - "comments": [ 1, 2 ] - }, - "comments": [{ - "id": 1, - "body": "FIRST" - }, { - "id": 2, - "body": "Rails is unagi" - }] - } - ``` - - The `normalize` method will be called three times: - - * With `App.Post`, `"posts"` and `{ id: 1, title: "Rails is omakase", ... }` - * With `App.Comment`, `"comments"` and `{ id: 1, body: "FIRST" }` - * With `App.Comment`, `"comments"` and `{ id: 2, body: "Rails is unagi" }` - - You can use this method, for example, to normalize underscored keys to camelized - or other general-purpose normalizations. - - If you want to do normalizations specific to some part of the payload, you - can specify those under `normalizeHash`. - - For example, if the `IDs` under `"comments"` are provided as `_id` instead of - `id`, you can specify how to normalize just the comments: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - normalizeHash: { - comments: function(hash) { - hash.id = hash._id; - delete hash._id; - return hash; - } - } - }); - ``` - - The key under `normalizeHash` is just the original key that was in the original - payload. - - @method normalize - @param {subclass of DS.Model} type - @param {Object} hash - @param {String} prop - @return {Object} - */ - normalize: function(type, hash, prop) { - this.normalizeId(hash); - this.normalizeAttributes(type, hash); - this.normalizeRelationships(type, hash); - - this.normalizeUsingDeclaredMapping(type, hash); - - if (this.normalizeHash && this.normalizeHash[prop]) { - this.normalizeHash[prop](hash); - } - - this.applyTransforms(type, hash); - return hash; - }, - - /** - You can use this method to normalize all payloads, regardless of whether they - represent single records or an array. - - For example, you might want to remove some extraneous data from the payload: - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - normalizePayload: function(payload) { - delete payload.version; - delete payload.status; - return payload; - } - }); - ``` - - @method normalizePayload - @param {Object} payload - @return {Object} the normalized payload - */ - normalizePayload: function(payload) { - return payload; - }, - - /** - @method normalizeAttributes - @private - */ - normalizeAttributes: function(type, hash) { - var payloadKey, key; - - if (this.keyForAttribute) { - type.eachAttribute(function(key) { - payloadKey = this.keyForAttribute(key); - if (key === payloadKey) { return; } - - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - }, this); - } - }, - - /** - @method normalizeRelationships - @private - */ - normalizeRelationships: function(type, hash) { - var payloadKey, key; - - if (this.keyForRelationship) { - type.eachRelationship(function(key, relationship) { - payloadKey = this.keyForRelationship(key, relationship.kind); - if (key === payloadKey) { return; } - - hash[key] = hash[payloadKey]; - delete hash[payloadKey]; - }, this); - } - }, - - /** - Called when the server has returned a payload representing - a single record, such as in response to a `find` or `save`. - - It is your opportunity to clean up the server's response into the normalized - form expected by Ember Data. - - If you want, you can just restructure the top-level of your payload, and - do more fine-grained normalization in the `normalize` method. - - For example, if you have a payload like this in response to a request for - post 1: - - ```js - { - "id": 1, - "title": "Rails is omakase", - - "_embedded": { - "comment": [{ - "_id": 1, - "comment_title": "FIRST" - }, { - "_id": 2, - "comment_title": "Rails is unagi" - }] - } - } - ``` - - You could implement a serializer that looks like this to get your payload - into shape: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - // First, restructure the top-level so it's organized by type - extractSingle: function(store, type, payload, id) { - var comments = payload._embedded.comment; - delete payload._embedded; - - payload = { comments: comments, post: payload }; - return this._super(store, type, payload, id); - }, - - normalizeHash: { - // Next, normalize individual comments, which (after `extract`) - // are now located under `comments` - comments: function(hash) { - hash.id = hash._id; - hash.title = hash.comment_title; - delete hash._id; - delete hash.comment_title; - return hash; - } - } - }) - ``` - - When you call super from your own implementation of `extractSingle`, the - built-in implementation will find the primary record in your normalized - payload and push the remaining records into the store. - - The primary record is the single hash found under `post` or the first - element of the `posts` array. - - The primary record has special meaning when the record is being created - for the first time or updated (`createRecord` or `updateRecord`). In - particular, it will update the properties of the record that was saved. - - @method extractSingle - @param {DS.Store} store - @param {subclass of DS.Model} primaryType - @param {Object} payload - @param {String} recordId - @return {Object} the primary response to the original request - */ - extractSingle: function(store, primaryType, rawPayload, recordId) { - var payload = this.normalizePayload(rawPayload); - var primaryTypeName = primaryType.typeKey; - var primaryRecord; - - for (var prop in payload) { - var typeName = this.typeForRoot(prop); - var type = store.modelFor(typeName); - var isPrimary = type.typeKey === primaryTypeName; - var value = payload[prop]; - - // legacy support for singular resources - if (isPrimary && Ember.typeOf(value) !== "array" ) { - primaryRecord = this.normalize(primaryType, value, prop); - continue; - } - - /*jshint loopfunc:true*/ - forEach.call(value, function(hash) { - var typeName = this.typeForRoot(prop); - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); - - hash = typeSerializer.normalize(type, hash, prop); - - var isFirstCreatedRecord = isPrimary && !recordId && !primaryRecord; - var isUpdatedRecord = isPrimary && coerceId(hash.id) === recordId; - - // find the primary record. - // - // It's either: - // * the record with the same ID as the original request - // * in the case of a newly created record that didn't have an ID, the first - // record in the Array - if (isFirstCreatedRecord || isUpdatedRecord) { - primaryRecord = hash; - } else { - store.push(typeName, hash); - } - }, this); - } - - return primaryRecord; - }, - - /** - Called when the server has returned a payload representing - multiple records, such as in response to a `findAll` or `findQuery`. - - It is your opportunity to clean up the server's response into the normalized - form expected by Ember Data. - - If you want, you can just restructure the top-level of your payload, and - do more fine-grained normalization in the `normalize` method. - - For example, if you have a payload like this in response to a request for - all posts: - - ```js - { - "_embedded": { - "post": [{ - "id": 1, - "title": "Rails is omakase" - }, { - "id": 2, - "title": "The Parley Letter" - }], - "comment": [{ - "_id": 1, - "comment_title": "Rails is unagi" - "post_id": 1 - }, { - "_id": 2, - "comment_title": "Don't tread on me", - "post_id": 2 - }] - } - } - ``` - - You could implement a serializer that looks like this to get your payload - into shape: - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - // First, restructure the top-level so it's organized by type - // and the comments are listed under a post's `comments` key. - extractArray: function(store, type, payload) { - var posts = payload._embedded.post; - var comments = []; - var postCache = {}; - - posts.forEach(function(post) { - post.comments = []; - postCache[post.id] = post; - }); - - payload._embedded.comment.forEach(function(comment) { - comments.push(comment); - postCache[comment.post_id].comments.push(comment); - delete comment.post_id; - } - - payload = { comments: comments, posts: payload }; - - return this._super(store, type, payload); - }, - - normalizeHash: { - // Next, normalize individual comments, which (after `extract`) - // are now located under `comments` - comments: function(hash) { - hash.id = hash._id; - hash.title = hash.comment_title; - delete hash._id; - delete hash.comment_title; - return hash; - } - } - }) - ``` - - When you call super from your own implementation of `extractArray`, the - built-in implementation will find the primary array in your normalized - payload and push the remaining records into the store. - - The primary array is the array found under `posts`. - - The primary record has special meaning when responding to `findQuery` - or `findHasMany`. In particular, the primary array will become the - list of records in the record array that kicked off the request. - - If your primary array contains secondary (embedded) records of the same type, - you cannot place these into the primary array `posts`. Instead, place the - secondary items into an underscore prefixed property `_posts`, which will - push these items into the store and will not affect the resulting query. - - @method extractArray - @param {DS.Store} store - @param {subclass of DS.Model} primaryType - @param {Object} payload - @return {Array} The primary array that was returned in response - to the original query. - */ - extractArray: function(store, primaryType, rawPayload) { - var payload = this.normalizePayload(rawPayload); - var primaryTypeName = primaryType.typeKey; - var primaryArray; - - for (var prop in payload) { - var typeKey = prop; - var forcedSecondary = false; - - if (prop.charAt(0) === '_') { - forcedSecondary = true; - typeKey = prop.substr(1); - } - - var typeName = this.typeForRoot(typeKey); - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); - var isPrimary = (!forcedSecondary && (type.typeKey === primaryTypeName)); - - /*jshint loopfunc:true*/ - var normalizedArray = map.call(payload[prop], function(hash) { - return typeSerializer.normalize(type, hash, prop); - }, this); - - if (isPrimary) { - primaryArray = normalizedArray; - } else { - store.pushMany(typeName, normalizedArray); - } - } - - return primaryArray; - }, - - /** - This method allows you to push a payload containing top-level - collections of records organized per type. - - ```js - { - "posts": [{ - "id": "1", - "title": "Rails is omakase", - "author", "1", - "comments": [ "1" ] - }], - "comments": [{ - "id": "1", - "body": "FIRST" - }], - "users": [{ - "id": "1", - "name": "@d2h" - }] - } - ``` - - It will first normalize the payload, so you can use this to push - in data streaming in from your server structured the same way - that fetches and saves are structured. - - @method pushPayload - @param {DS.Store} store - @param {Object} payload - */ - pushPayload: function(store, rawPayload) { - var payload = this.normalizePayload(rawPayload); - - for (var prop in payload) { - var typeName = this.typeForRoot(prop); - var type = store.modelFor(typeName); - var typeSerializer = store.serializerFor(type); - - /*jshint loopfunc:true*/ - var normalizedArray = map.call(Ember.makeArray(payload[prop]), function(hash) { - return typeSerializer.normalize(type, hash, prop); - }, this); - - store.pushMany(typeName, normalizedArray); - } - }, - - /** - This method is used to convert each JSON root key in the payload - into a typeKey that it can use to look up the appropriate model for - that part of the payload. By default the typeKey for a model is its - name in camelCase, so if your JSON root key is 'fast-car' you would - use typeForRoot to convert it to 'fastCar' so that Ember Data finds - the `FastCar` model. - - If you diverge from this norm you should also consider changes to - store._normalizeTypeKey as well. - - For example, your server may return prefixed root keys like so: - - ```js - { - "response-fast-car": { - "id": "1", - "name": "corvette" - } - } - ``` - - In order for Ember Data to know that the model corresponding to - the 'response-fast-car' hash is `FastCar` (typeKey: 'fastCar'), - you can override typeForRoot to convert 'response-fast-car' to - 'fastCar' like so: - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - typeForRoot: function(root) { - // 'response-fast-car' should become 'fast-car' - var subRoot = root.substring(9); - - // _super normalizes 'fast-car' to 'fastCar' - return this._super(subRoot); - } - }); - ``` - - @method typeForRoot - @param {String} key - @return {String} the model's typeKey - */ - typeForRoot: function(key) { - return camelize(singularize(key)); - }, - - // SERIALIZE - - /** - Called when a record is saved in order to convert the - record into JSON. - - By default, it creates a JSON object with a key for - each attribute and belongsTo relationship. - - For example, consider this model: - - ```js - App.Comment = DS.Model.extend({ - title: DS.attr(), - body: DS.attr(), - - author: DS.belongsTo('user') - }); - ``` - - The default serialization would create a JSON object like: - - ```js - { - "title": "Rails is unagi", - "body": "Rails? Omakase? O_O", - "author": 12 - } - ``` - - By default, attributes are passed through as-is, unless - you specified an attribute type (`DS.attr('date')`). If - you specify a transform, the JavaScript value will be - serialized when inserted into the JSON hash. - - By default, belongs-to relationships are converted into - IDs when inserted into the JSON hash. - - ## IDs - - `serialize` takes an options hash with a single option: - `includeId`. If this option is `true`, `serialize` will, - by default include the ID in the JSON object it builds. - - The adapter passes in `includeId: true` when serializing - a record for `createRecord`, but not for `updateRecord`. - - ## Customization - - Your server may expect a different JSON format than the - built-in serialization format. - - In that case, you can implement `serialize` yourself and - return a JSON hash of your choosing. - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - serialize: function(post, options) { - var json = { - POST_TTL: post.get('title'), - POST_BDY: post.get('body'), - POST_CMS: post.get('comments').mapBy('id') - } - - if (options.includeId) { - json.POST_ID_ = post.get('id'); - } - - return json; - } - }); - ``` - - ## Customizing an App-Wide Serializer - - If you want to define a serializer for your entire - application, you'll probably want to use `eachAttribute` - and `eachRelationship` on the record. - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - serialize: function(record, options) { - var json = {}; - - record.eachAttribute(function(name) { - json[serverAttributeName(name)] = record.get(name); - }) - - record.eachRelationship(function(name, relationship) { - if (relationship.kind === 'hasMany') { - json[serverHasManyName(name)] = record.get(name).mapBy('id'); - } - }); - - if (options.includeId) { - json.ID_ = record.get('id'); - } - - return json; - } - }); - - function serverAttributeName(attribute) { - return attribute.underscore().toUpperCase(); - } - - function serverHasManyName(name) { - return serverAttributeName(name.singularize()) + "_IDS"; - } - ``` - - This serializer will generate JSON that looks like this: - - ```js - { - "TITLE": "Rails is omakase", - "BODY": "Yep. Omakase.", - "COMMENT_IDS": [ 1, 2, 3 ] - } - ``` - - ## Tweaking the Default JSON - - If you just want to do some small tweaks on the default JSON, - you can call super first and make the tweaks on the returned - JSON. - - ```js - App.PostSerializer = DS.RESTSerializer.extend({ - serialize: function(record, options) { - var json = this._super(record, options); - - json.subject = json.title; - delete json.title; - - return json; - } - }); - ``` - - @method serialize - @param record - @param options - */ - serialize: function(record, options) { - return this._super.apply(this, arguments); - }, - - /** - You can use this method to customize the root keys serialized into the JSON. - By default the REST Serializer sends the typeKey of a model, whih is a camelized - version of the name. - - For example, your server may expect underscored root objects. - - ```js - App.ApplicationSerializer = DS.RESTSerializer.extend({ - serializeIntoHash: function(data, type, record, options) { - var root = Ember.String.decamelize(type.typeKey); - data[root] = this.serialize(record, options); - } - }); - ``` - - @method serializeIntoHash - @param {Object} hash - @param {subclass of DS.Model} type - @param {DS.Model} record - @param {Object} options - */ - serializeIntoHash: function(hash, type, record, options) { - hash[type.typeKey] = this.serialize(record, options); - }, - - /** - You can use this method to customize how polymorphic objects are serialized. - By default the JSON Serializer creates the key by appending `Type` to - the attribute and value from the model's camelcased model name. - - @method serializePolymorphicType - @param {DS.Model} record - @param {Object} json - @param {Object} relationship - */ - serializePolymorphicType: function(record, json, relationship) { - var key = relationship.key; - var belongsTo = get(record, key); - key = this.keyForAttribute ? this.keyForAttribute(key) : key; - json[key + "Type"] = belongsTo.constructor.typeKey; - } - }); - }); -define("ember-data/lib/setup-container", - ["./initializers/store","./initializers/transforms","./initializers/store_injections","./initializers/data_adapter","../../../activemodel-adapter/lib/setup-container","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var initializeStore = __dependency1__["default"]; - var initializeTransforms = __dependency2__["default"]; - var initializeStoreInjections = __dependency3__["default"]; - var initializeDataAdapter = __dependency4__["default"]; - var setupActiveModelContainer = __dependency5__["default"]; - - __exports__["default"] = function setupContainer(container, application){ - // application is not a required argument. This ensures - // testing setups can setup a container without booting an - // entire ember application. - - initializeDataAdapter(container, application); - initializeTransforms(container, application); - initializeStoreInjections(container, application); - initializeStore(container, application); - setupActiveModelContainer(container, application); - }; - }); -define("ember-data/lib/system/adapter", - ["exports"], - function(__exports__) { - "use strict"; - /** - @module ember-data - */ - - var get = Ember.get; - var set = Ember.set; - var map = Ember.ArrayPolyfills.map; - - var errorProps = [ - 'description', - 'fileName', - 'lineNumber', - 'message', - 'name', - 'number', - 'stack' - ]; - - /** - A `DS.InvalidError` is used by an adapter to signal the external API - was unable to process a request because the content was not - semantically correct or meaningful per the API. Usually this means a - record failed some form of server side validation. When a promise - from an adapter is rejected with a `DS.InvalidError` the record will - transition to the `invalid` state and the errors will be set to the - `errors` property on the record. - - Example - - ```javascript - App.ApplicationAdapter = DS.RESTAdapter.extend({ - ajaxError: function(jqXHR) { - var error = this._super(jqXHR); - - if (jqXHR && jqXHR.status === 422) { - var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"]; - return new DS.InvalidError(jsonErrors); - } else { - return error; - } - } - }); - ``` - - The `DS.InvalidError` must be constructed with a single object whose - keys are the invalid model properties, and whose values are the - corresponding error messages. For example: - - ```javascript - return new DS.InvalidError({ - length: 'Must be less than 15', - name: 'Must not be blank - }); - ``` - - @class InvalidError - @namespace DS - */ - function InvalidError(errors) { - var tmp = Error.prototype.constructor.call(this, "The backend rejected the commit because it was invalid: " + Ember.inspect(errors)); - this.errors = errors; - - for (var i=0, l=errorProps.length; i 0; i--) { - var proxyPair = proxyPairs[i - 1], - deprecated = proxyPair['deprecated'], - valid = proxyPair['valid']; - - this.registerDeprecation(deprecated, valid); - } - }; - - __exports__["default"] = ContainerProxy; - }); -define("ember-data/lib/system/debug", - ["./debug/debug_info","./debug/debug_adapter","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var DebugAdapter = __dependency2__["default"]; - - __exports__["default"] = DebugAdapter; - }); -define("ember-data/lib/system/debug/debug_adapter", - ["../model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - var Model = __dependency1__.Model; - var get = Ember.get; - var capitalize = Ember.String.capitalize; - var underscore = Ember.String.underscore; - - /** - Extend `Ember.DataAdapter` with ED specific code. - - @class DebugAdapter - @namespace DS - @extends Ember.DataAdapter - @private - */ - __exports__["default"] = Ember.DataAdapter.extend({ - getFilters: function() { - return [ - { name: 'isNew', desc: 'New' }, - { name: 'isModified', desc: 'Modified' }, - { name: 'isClean', desc: 'Clean' } - ]; - }, - - detect: function(klass) { - return klass !== Model && Model.detect(klass); - }, - - columnsForType: function(type) { - var columns = [{ - name: 'id', - desc: 'Id' - }]; - var count = 0; - var self = this; - get(type, 'attributes').forEach(function(name, meta) { - if (count++ > self.attributeLimit) { return false; } - var desc = capitalize(underscore(name).replace('_', ' ')); - columns.push({ name: name, desc: desc }); - }); - return columns; - }, - - getRecords: function(type) { - return this.get('store').all(type); - }, - - getRecordColumnValues: function(record) { - var self = this, count = 0; - var columnValues = { id: get(record, 'id') }; - - record.eachAttribute(function(key) { - if (count++ > self.attributeLimit) { - return false; - } - var value = get(record, key); - columnValues[key] = value; - }); - return columnValues; - }, - - getRecordKeywords: function(record) { - var keywords = []; - var keys = Ember.A(['id']); - record.eachAttribute(function(key) { - keys.push(key); - }); - keys.forEach(function(key) { - keywords.push(get(record, key)); - }); - return keywords; - }, - - getRecordFilterValues: function(record) { - return { - isNew: record.get('isNew'), - isModified: record.get('isDirty') && !record.get('isNew'), - isClean: !record.get('isDirty') - }; - }, - - getRecordColor: function(record) { - var color = 'black'; - if (record.get('isNew')) { - color = 'green'; - } else if (record.get('isDirty')) { - color = 'blue'; - } - return color; - }, - - observeRecord: function(record, recordUpdated) { - var releaseMethods = Ember.A(), self = this, - keysToObserve = Ember.A(['id', 'isNew', 'isDirty']); - - record.eachAttribute(function(key) { - keysToObserve.push(key); - }); - - keysToObserve.forEach(function(key) { - var handler = function() { - recordUpdated(self.wrapRecord(record)); - }; - Ember.addObserver(record, key, handler); - releaseMethods.push(function() { - Ember.removeObserver(record, key, handler); - }); - }); - - var release = function() { - releaseMethods.forEach(function(fn) { fn(); } ); - }; - - return release; - } - - }); - }); -define("ember-data/lib/system/debug/debug_info", - ["../model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Model = __dependency1__.Model; - - Model.reopen({ - - /** - Provides info about the model for debugging purposes - by grouping the properties into more semantic groups. - - Meant to be used by debugging tools such as the Chrome Ember Extension. - - - Groups all attributes in "Attributes" group. - - Groups all belongsTo relationships in "Belongs To" group. - - Groups all hasMany relationships in "Has Many" group. - - Groups all flags in "Flags" group. - - Flags relationship CPs as expensive properties. - - @method _debugInfo - @for DS.Model - @private - */ - _debugInfo: function() { - var attributes = ['id'], - relationships = { belongsTo: [], hasMany: [] }, - expensiveProperties = []; - - this.eachAttribute(function(name, meta) { - attributes.push(name); - }, this); - - this.eachRelationship(function(name, relationship) { - relationships[relationship.kind].push(name); - expensiveProperties.push(name); - }); - - var groups = [ - { - name: 'Attributes', - properties: attributes, - expand: true - }, - { - name: 'Belongs To', - properties: relationships.belongsTo, - expand: true - }, - { - name: 'Has Many', - properties: relationships.hasMany, - expand: true - }, - { - name: 'Flags', - properties: ['isLoaded', 'isDirty', 'isSaving', 'isDeleted', 'isError', 'isNew', 'isValid'] - } - ]; - - return { - propertyInfo: { - // include all other mixins / properties (not just the grouped ones) - includeOtherProperties: true, - groups: groups, - // don't pre-calculate unless cached - expensiveProperties: expensiveProperties - } - }; - } - }); - - __exports__["default"] = Model; - }); -define("ember-data/lib/system/model", - ["./model/model","./model/attributes","./model/states","./model/errors","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var Model = __dependency1__["default"]; - var attr = __dependency2__["default"]; - var RootState = __dependency3__["default"]; - var Errors = __dependency4__["default"]; - - __exports__.Model = Model; - __exports__.RootState = RootState; - __exports__.attr = attr; - __exports__.Errors = Errors; - }); -define("ember-data/lib/system/model/attributes", - ["./model","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Model = __dependency1__["default"]; - - /** - @module ember-data - */ - - var get = Ember.get; - - /** - @class Model - @namespace DS - */ - Model.reopenClass({ - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are the meta object for the - property. - - Example - - ```javascript - - App.Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); - - var attributes = Ember.get(App.Person, 'attributes') - - attributes.forEach(function(name, meta) { - console.log(name, meta); - }); - - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` - - @property attributes - @static - @type {Ember.Map} - @readOnly - */ - attributes: Ember.computed(function() { - var map = Ember.Map.create(); - - this.eachComputedProperty(function(name, meta) { - if (meta.isAttribute) { - Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.toString(), name !== 'id'); - - meta.name = name; - map.set(name, meta); - } - }); - - return map; - }), - - /** - A map whose keys are the attributes of the model (properties - described by DS.attr) and whose values are type of transformation - applied to each attribute. This map does not include any - attributes that do not have an transformation type. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); - - var transformedAttributes = Ember.get(App.Person, 'transformedAttributes') - - transformedAttributes.forEach(function(field, type) { - console.log(field, type); - }); - - // prints: - // lastName string - // birthday date - ``` - - @property transformedAttributes - @static - @type {Ember.Map} - @readOnly - */ - transformedAttributes: Ember.computed(function() { - var map = Ember.Map.create(); - - this.eachAttribute(function(key, meta) { - if (meta.type) { - map.set(key, meta.type); - } - }); - - return map; - }), - - /** - Iterates through the attributes of the model, calling the passed function on each - attribute. - - The callback method you provide should have the following signature (all - parameters are optional): - - ```javascript - function(name, meta); - ``` - - - `name` the name of the current property in the iteration - - `meta` the meta object for the attribute property in the iteration - - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: attr('string'), - lastName: attr('string'), - birthday: attr('date') - }); - - App.Person.eachAttribute(function(name, meta) { - console.log(name, meta); - }); - - // prints: - // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"} - // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"} - // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"} - ``` - - @method eachAttribute - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @static - */ - eachAttribute: function(callback, binding) { - get(this, 'attributes').forEach(function(name, meta) { - callback.call(binding, name, meta); - }, binding); - }, - - /** - Iterates through the transformedAttributes of the model, calling - the passed function on each attribute. Note the callback will not be - called for any attributes that do not have an transformation type. - - The callback method you provide should have the following signature (all - parameters are optional): - - ```javascript - function(name, type); - ``` - - - `name` the name of the current property in the iteration - - `type` a string containing the name of the type of transformed - applied to the attribute - - Note that in addition to a callback, you can also pass an optional target - object that will be set as `this` on the context. - - Example - - ```javascript - App.Person = DS.Model.extend({ - firstName: attr(), - lastName: attr('string'), - birthday: attr('date') - }); - - App.Person.eachTransformedAttribute(function(name, type) { - console.log(name, type); - }); - - // prints: - // lastName string - // birthday date - ``` - - @method eachTransformedAttribute - @param {Function} callback The callback to execute - @param {Object} [target] The target object to use - @static - */ - eachTransformedAttribute: function(callback, binding) { - get(this, 'transformedAttributes').forEach(function(name, type) { - callback.call(binding, name, type); - }); - } - }); - - - Model.reopen({ - eachAttribute: function(callback, binding) { - this.constructor.eachAttribute(callback, binding); - } - }); - - function getDefaultValue(record, options, key) { - if (typeof options.defaultValue === "function") { - return options.defaultValue.apply(null, arguments); - } else { - return options.defaultValue; - } - } - - function hasValue(record, key) { - return record._attributes.hasOwnProperty(key) || - record._inFlightAttributes.hasOwnProperty(key) || - record._data.hasOwnProperty(key); - } - - function getValue(record, key) { - if (record._attributes.hasOwnProperty(key)) { - return record._attributes[key]; - } else if (record._inFlightAttributes.hasOwnProperty(key)) { - return record._inFlightAttributes[key]; - } else { - return record._data[key]; - } - } - - /** - `DS.attr` defines an attribute on a [DS.Model](/api/data/classes/DS.Model.html). - By default, attributes are passed through as-is, however you can specify an - optional type to have the value automatically transformed. - Ember Data ships with four basic transform types: `string`, `number`, - `boolean` and `date`. You can define your own transforms by subclassing - [DS.Transform](/api/data/classes/DS.Transform.html). - - Note that you cannot use `attr` to define an attribute of `id`. - - `DS.attr` takes an optional hash as a second parameter, currently - supported options are: - - - `defaultValue`: Pass a string or a function to be called to set the attribute - to a default value if none is supplied. - - Example - - ```javascript - var attr = DS.attr; - - App.User = DS.Model.extend({ - username: attr('string'), - email: attr('string'), - verified: attr('boolean', {defaultValue: false}) - }); - ``` - - @namespace - @method attr - @for DS - @param {String} type the attribute type - @param {Object} options a hash of options - @return {Attribute} - */ - - __exports__["default"] = function attr(type, options) { - options = options || {}; - - var meta = { - type: type, - isAttribute: true, - options: options - }; - - return Ember.computed('data', function(key, value) { - if (arguments.length > 1) { - Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.constructor.toString(), key !== 'id'); - var oldValue = getValue(this, key); - - if (value !== oldValue) { - // Add the new value to the changed attributes hash; it will get deleted by - // the 'didSetProperty' handler if it is no different from the original value - this._attributes[key] = value; - - this.send('didSetProperty', { - name: key, - oldValue: oldValue, - originalValue: this._data[key], - value: value - }); - } - - return value; - } else if (hasValue(this, key)) { - return getValue(this, key); - } else { - return getDefaultValue(this, options, key); - } - - // `data` is never set directly. However, it may be - // invalidated from the state manager's setData - // event. - }).meta(meta); - }; - }); -define("ember-data/lib/system/model/errors", - ["exports"], - function(__exports__) { - "use strict"; - var get = Ember.get; - var isEmpty = Ember.isEmpty; - var map = Ember.EnumerableUtils.map; - - /** - @module ember-data - */ - - /** - Holds validation errors for a given record organized by attribute names. - - Every DS.Model has an `errors` property that is an instance of - `DS.Errors`. This can be used to display validation error - messages returned from the server when a `record.save()` rejects. - This works automatically with `DS.ActiveModelAdapter`, but you - can implement [ajaxError](api/data/classes/DS.RESTAdapter.html#method_ajaxError) - in other adapters as well. - - For Example, if you had an `User` model that looked like this: - - ```javascript - App.User = DS.Model.extend({ - username: attr('string'), - email: attr('string') - }); - ``` - And you attempted to save a record that did not validate on the backend. - - ```javascript - var user = store.createRecord('user', { - username: 'tomster', - email: 'invalidEmail' - }); - user.save(); - ``` - - Your backend data store might return a response that looks like - this. This response will be used to populate the error object. - - ```javascript - { - "errors": { - "username": ["This username is already taken!"], - "email": ["Doesn't look like a valid email."] - } - } - ``` - - Errors can be displayed to the user by accessing their property name - or using the `messages` property to get an array of all errors. - - ```handlebars - {{#each errors.messages}} -
- {{message}} -
- {{/each}} - - - {{#each errors.username}} -
- {{message}} -
- {{/each}} - - - {{#each errors.email}} -
- {{message}} -
- {{/each}} - ``` - - @class Errors - @namespace DS - @extends Ember.Object - @uses Ember.Enumerable - @uses Ember.Evented - */ - __exports__["default"] = Ember.Object.extend(Ember.Enumerable, Ember.Evented, { - /** - Register with target handler - - @method registerHandlers - @param {Object} target - @param {Function} becameInvalid - @param {Function} becameValid - */ - registerHandlers: function(target, becameInvalid, becameValid) { - this.on('becameInvalid', target, becameInvalid); - this.on('becameValid', target, becameValid); - }, - - /** - @property errorsByAttributeName - @type {Ember.MapWithDefault} - @private - */ - errorsByAttributeName: Ember.reduceComputed("content", { - initialValue: function() { - return Ember.MapWithDefault.create({ - defaultValue: function() { - return Ember.A(); - } - }); - }, - - addedItem: function(errors, error) { - errors.get(error.attribute).pushObject(error); - - return errors; - }, - - removedItem: function(errors, error) { - errors.get(error.attribute).removeObject(error); - - return errors; - } - }), - - /** - Returns errors for a given attribute - - ```javascript - var user = store.createRecord('user', { - username: 'tomster', - email: 'invalidEmail' - }); - user.save().catch(function(){ - user.get('errors').errorsFor('email'); // ["Doesn't look like a valid email."] - }); - ``` - - @method errorsFor - @param {String} attribute - @return {Array} - */ - errorsFor: function(attribute) { - return get(this, 'errorsByAttributeName').get(attribute); - }, - - /** - An array containing all of the error messages for this - record. This is useful for displaying all errors to the user. - - ```handlebars - {{#each errors.messages}} -
- {{message}} -
- {{/each}} - ``` - - @property messages - @type {Array} - */ - messages: Ember.computed.mapBy('content', 'message'), - - /** - @property content - @type {Array} - @private - */ - content: Ember.computed(function() { - return Ember.A(); - }), - - /** - @method unknownProperty - @private - */ - unknownProperty: function(attribute) { - var errors = this.errorsFor(attribute); - if (isEmpty(errors)) { return null; } - return errors; - }, - - /** - @method nextObject - @private - */ - nextObject: function(index, previousObject, context) { - return get(this, 'content').objectAt(index); - }, - - /** - Total number of errors. - - @property length - @type {Number} - @readOnly - */ - length: Ember.computed.oneWay('content.length').readOnly(), - - /** - @property isEmpty - @type {Boolean} - @readOnly - */ - isEmpty: Ember.computed.not('length').readOnly(), - - /** - Adds error messages to a given attribute and sends - `becameInvalid` event to the record. - - Example: - - ```javascript - if (!user.get('username') { - user.get('errors').add('username', 'This field is required'); - } - ``` - - @method add - @param {String} attribute - @param {Array|String} messages - */ - add: function(attribute, messages) { - var wasEmpty = get(this, 'isEmpty'); - - messages = this._findOrCreateMessages(attribute, messages); - get(this, 'content').addObjects(messages); - - this.notifyPropertyChange(attribute); - this.enumerableContentDidChange(); - - if (wasEmpty && !get(this, 'isEmpty')) { - this.trigger('becameInvalid'); - } - }, - - /** - @method _findOrCreateMessages - @private - */ - _findOrCreateMessages: function(attribute, messages) { - var errors = this.errorsFor(attribute); - - return map(Ember.makeArray(messages), function(message) { - return errors.findBy('message', message) || { - attribute: attribute, - message: message - }; - }); - }, - - /** - Removes all error messages from the given attribute and sends - `becameValid` event to the record if there no more errors left. - - Example: - - ```javascript - App.User = DS.Model.extend({ - email: DS.attr('string'), - twoFactorAuth: DS.attr('boolean'), - phone: DS.attr('string') - }); - - App.UserEditRoute = Ember.Route.extend({ - actions: { - save: function(user) { - if (!user.get('twoFactorAuth')) { - user.get('errors').remove('phone'); - } - user.save(); - } - } - }); - ``` - - @method remove - @param {String} attribute - */ - remove: function(attribute) { - if (get(this, 'isEmpty')) { return; } - - var content = get(this, 'content').rejectBy('attribute', attribute); - get(this, 'content').setObjects(content); - - this.notifyPropertyChange(attribute); - this.enumerableContentDidChange(); - - if (get(this, 'isEmpty')) { - this.trigger('becameValid'); - } - }, - - /** - Removes all error messages and sends `becameValid` event - to the record. - - Example: - - ```javascript - App.UserEditRoute = Ember.Route.extend({ - actions: { - retrySave: function(user) { - user.get('errors').clear(); - user.save(); - } - } - }); - ``` - - @method clear - */ - clear: function() { - if (get(this, 'isEmpty')) { return; } - - get(this, 'content').clear(); - this.enumerableContentDidChange(); - - this.trigger('becameValid'); - }, - - /** - Checks if there is error messages for the given attribute. - - ```javascript - App.UserEditRoute = Ember.Route.extend({ - actions: { - save: function(user) { - if (user.get('errors').has('email')) { - return alert('Please update your email before attempting to save.'); - } - user.save(); - } - } - }); - ``` - - @method has - @param {String} attribute - @return {Boolean} true if there some errors on given attribute - */ - has: function(attribute) { - return !isEmpty(this.errorsFor(attribute)); - } - }); - }); -define("ember-data/lib/system/model/model", - ["./states","./errors","../store","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var RootState = __dependency1__["default"]; - var Errors = __dependency2__["default"]; - var PromiseObject = __dependency3__.PromiseObject; - /** - @module ember-data - */ - - var get = Ember.get; - var set = Ember.set; - var merge = Ember.merge; - var Promise = Ember.RSVP.Promise; - - var JSONSerializer; - var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) { - return get(get(this, 'currentState'), key); - }).readOnly(); - - /** - - The model class that all Ember Data records descend from. - - @class Model - @namespace DS - @extends Ember.Object - @uses Ember.Evented - */ - var Model = Ember.Object.extend(Ember.Evented, { - _recordArrays: undefined, - _relationships: undefined, - _loadingRecordArrays: undefined, - /** - If this property is `true` the record is in the `empty` - state. Empty is the first state all records enter after they have - been created. Most records created by the store will quickly - transition to the `loading` state if data needs to be fetched from - the server or the `created` state if the record is created on the - client. A record can also enter the empty state if the adapter is - unable to locate the record. - - @property isEmpty - @type {Boolean} - @readOnly - */ - isEmpty: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `loading` state. A - record enters this state when the store asks the adapter for its - data. It remains in this state until the adapter provides the - requested data. - - @property isLoading - @type {Boolean} - @readOnly - */ - isLoading: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `loaded` state. A - record enters this state when its data is populated. Most of a - record's lifecycle is spent inside substates of the `loaded` - state. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isLoaded'); // true - - store.find('model', 1).then(function(model) { - model.get('isLoaded'); // true - }); - ``` - - @property isLoaded - @type {Boolean} - @readOnly - */ - isLoaded: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `dirty` state. The - record has local changes that have not yet been saved by the - adapter. This includes records that have been created (but not yet - saved) or deleted. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isDirty'); // true - - store.find('model', 1).then(function(model) { - model.get('isDirty'); // false - model.set('foo', 'some value'); - model.get('isDirty'); // true - }); - ``` - - @property isDirty - @type {Boolean} - @readOnly - */ - isDirty: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `saving` state. A - record enters the saving state when `save` is called, but the - adapter has not yet acknowledged that the changes have been - persisted to the backend. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isSaving'); // false - var promise = record.save(); - record.get('isSaving'); // true - promise.then(function() { - record.get('isSaving'); // false - }); - ``` - - @property isSaving - @type {Boolean} - @readOnly - */ - isSaving: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `deleted` state - and has been marked for deletion. When `isDeleted` is true and - `isDirty` is true, the record is deleted locally but the deletion - was not yet persisted. When `isSaving` is true, the change is - in-flight. When both `isDirty` and `isSaving` are false, the - change has persisted. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isDeleted'); // false - record.deleteRecord(); - - // Locally deleted - record.get('isDeleted'); // true - record.get('isDirty'); // true - record.get('isSaving'); // false - - // Persisting the deletion - var promise = record.save(); - record.get('isDeleted'); // true - record.get('isSaving'); // true - - // Deletion Persisted - promise.then(function() { - record.get('isDeleted'); // true - record.get('isSaving'); // false - record.get('isDirty'); // false - }); - ``` - - @property isDeleted - @type {Boolean} - @readOnly - */ - isDeleted: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `new` state. A - record will be in the `new` state when it has been created on the - client and the adapter has not yet report that it was successfully - saved. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('isNew'); // true - - record.save().then(function(model) { - model.get('isNew'); // false - }); - ``` - - @property isNew - @type {Boolean} - @readOnly - */ - isNew: retrieveFromCurrentState, - /** - If this property is `true` the record is in the `valid` state. - - A record will be in the `valid` state when the adapter did not report any - server-side validation failures. - - @property isValid - @type {Boolean} - @readOnly - */ - isValid: retrieveFromCurrentState, - /** - If the record is in the dirty state this property will report what - kind of change has caused it to move into the dirty - state. Possible values are: - - - `created` The record has been created by the client and not yet saved to the adapter. - - `updated` The record has been updated by the client and not yet saved to the adapter. - - `deleted` The record has been deleted by the client and not yet saved to the adapter. - - Example - - ```javascript - var record = store.createRecord('model'); - record.get('dirtyType'); // 'created' - ``` - - @property dirtyType - @type {String} - @readOnly - */ - dirtyType: retrieveFromCurrentState, - - /** - If `true` the adapter reported that it was unable to save local - changes to the backend for any reason other than a server-side - validation error. - - Example - - ```javascript - record.get('isError'); // false - record.set('foo', 'valid value'); - record.save().then(null, function() { - record.get('isError'); // true - }); - ``` - - @property isError - @type {Boolean} - @readOnly - */ - isError: false, - /** - If `true` the store is attempting to reload the record form the adapter. - - Example - - ```javascript - record.get('isReloading'); // false - record.reload(); - record.get('isReloading'); // true - ``` - - @property isReloading - @type {Boolean} - @readOnly - */ - isReloading: false, - - /** - The `clientId` property is a transient numerical identifier - generated at runtime by the data store. It is important - primarily because newly created objects may not yet have an - externally generated id. - - @property clientId - @private - @type {Number|String} - */ - clientId: null, - /** - All ember models have an id property. This is an identifier - managed by an external source. These are always coerced to be - strings before being used internally. Note when declaring the - attributes for a model it is an error to declare an id - attribute. - - ```javascript - var record = store.createRecord('model'); - record.get('id'); // null - - store.find('model', 1).then(function(model) { - model.get('id'); // '1' - }); - ``` - - @property id - @type {String} - */ - id: null, - - /** - @property currentState - @private - @type {Object} - */ - currentState: RootState.empty, - - /** - When the record is in the `invalid` state this object will contain - any errors returned by the adapter. When present the errors hash - typically contains keys corresponding to the invalid property names - and values which are an array of error messages. - - ```javascript - record.get('errors.length'); // 0 - record.set('foo', 'invalid value'); - record.save().then(null, function() { - record.get('errors').get('foo'); // ['foo should be a number.'] - }); - ``` - - @property errors - @type {DS.Errors} - */ - errors: Ember.computed(function() { - var errors = Errors.create(); - - errors.registerHandlers(this, function() { - this.send('becameInvalid'); - }, function() { - this.send('becameValid'); - }); - - return errors; - }).readOnly(), - - /** - Create a JSON representation of the record, using the serialization - strategy of the store's adapter. - - `serialize` takes an optional hash as a parameter, currently - supported options are: - - - `includeId`: `true` if the record's ID should be included in the - JSON representation. - - @method serialize - @param {Object} options - @return {Object} an object whose values are primitive JSON values only - */ - serialize: function(options) { - var store = get(this, 'store'); - return store.serialize(this, options); - }, - - /** - Use [DS.JSONSerializer](DS.JSONSerializer.html) to - get the JSON representation of a record. - - `toJSON` takes an optional hash as a parameter, currently - supported options are: - - - `includeId`: `true` if the record's ID should be included in the - JSON representation. - - @method toJSON - @param {Object} options - @return {Object} A JSON representation of the object. - */ - toJSON: function(options) { - if (!JSONSerializer) { JSONSerializer = requireModule("ember-data/lib/serializers/json_serializer")["default"]; } - // container is for lazy transform lookups - var serializer = JSONSerializer.create({ container: this.container }); - return serializer.serialize(this, options); - }, - - /** - Fired when the record is loaded from the server. - - @event didLoad - */ - didLoad: Ember.K, - - /** - Fired when the record is updated. - - @event didUpdate - */ - didUpdate: Ember.K, - - /** - Fired when the record is created. - - @event didCreate - */ - didCreate: Ember.K, - - /** - Fired when the record is deleted. - - @event didDelete - */ - didDelete: Ember.K, - - /** - Fired when the record becomes invalid. - - @event becameInvalid - */ - becameInvalid: Ember.K, - - /** - Fired when the record enters the error state. - - @event becameError - */ - becameError: Ember.K, - - /** - @property data - @private - @type {Object} - */ - data: Ember.computed(function() { - this._data = this._data || {}; - return this._data; - }).readOnly(), - - _data: null, - - init: function() { - this._super(); - this._setup(); - }, - - _setup: function() { - this._changesToSync = {}; - this._deferredTriggers = []; - this._data = {}; - this._attributes = {}; - this._inFlightAttributes = {}; - this._relationships = {}; - }, - - /** - @method send - @private - @param {String} name - @param {Object} context - */ - send: function(name, context) { - var currentState = get(this, 'currentState'); - - if (!currentState[name]) { - this._unhandledEvent(currentState, name, context); - } - - return currentState[name](this, context); - }, - - /** - @method transitionTo - @private - @param {String} name - */ - transitionTo: function(name) { - // POSSIBLE TODO: Remove this code and replace with - // always having direct references to state objects - - var pivotName = name.split(".", 1), - currentState = get(this, 'currentState'), - state = currentState; - - do { - if (state.exit) { state.exit(this); } - state = state.parentState; - } while (!state.hasOwnProperty(pivotName)); - - var path = name.split("."); - - var setups = [], enters = [], i, l; - - for (i=0, l=path.length; i "root.created.uncommitted" - ``` - - The hierarchy of valid states that ship with ember data looks like - this: - - ```text - * root - * deleted - * saved - * uncommitted - * inFlight - * empty - * loaded - * created - * uncommitted - * inFlight - * saved - * updated - * uncommitted - * inFlight - * loading - ``` - - The `DS.Model` states are themselves stateless. What that means is - that, the hierarchical states that each of *those* points to is a - shared data structure. For performance reasons, instead of each - record getting its own copy of the hierarchy of states, each record - points to this global, immutable shared instance. How does a state - know which record it should be acting on? We pass the record - instance into the state's event handlers as the first argument. - - The record passed as the first parameter is where you should stash - state about the record if needed; you should never store data on the state - object itself. - - ### Events and Flags - - A state may implement zero or more events and flags. - - #### Events - - Events are named functions that are invoked when sent to a record. The - record will first look for a method with the given name on the - current state. If no method is found, it will search the current - state's parent, and then its grandparent, and so on until reaching - the top of the hierarchy. If the root is reached without an event - handler being found, an exception will be raised. This can be very - helpful when debugging new features. - - Here's an example implementation of a state with a `myEvent` event handler: - - ```javascript - aState: DS.State.create({ - myEvent: function(manager, param) { - console.log("Received myEvent with", param); - } - }) - ``` - - To trigger this event: - - ```javascript - record.send('myEvent', 'foo'); - //=> "Received myEvent with foo" - ``` - - Note that an optional parameter can be sent to a record's `send()` method, - which will be passed as the second parameter to the event handler. - - Events should transition to a different state if appropriate. This can be - done by calling the record's `transitionTo()` method with a path to the - desired state. The state manager will attempt to resolve the state path - relative to the current state. If no state is found at that path, it will - attempt to resolve it relative to the current state's parent, and then its - parent, and so on until the root is reached. For example, imagine a hierarchy - like this: - - * created - * uncommitted <-- currentState - * inFlight - * updated - * inFlight - - If we are currently in the `uncommitted` state, calling - `transitionTo('inFlight')` would transition to the `created.inFlight` state, - while calling `transitionTo('updated.inFlight')` would transition to - the `updated.inFlight` state. - - Remember that *only events* should ever cause a state transition. You should - never call `transitionTo()` from outside a state's event handler. If you are - tempted to do so, create a new event and send that to the state manager. - - #### Flags - - Flags are Boolean values that can be used to introspect a record's current - state in a more user-friendly way than examining its state path. For example, - instead of doing this: - - ```javascript - var statePath = record.get('stateManager.currentPath'); - if (statePath === 'created.inFlight') { - doSomething(); - } - ``` - - You can say: - - ```javascript - if (record.get('isNew') && record.get('isSaving')) { - doSomething(); - } - ``` - - If your state does not set a value for a given flag, the value will - be inherited from its parent (or the first place in the state hierarchy - where it is defined). - - The current set of flags are defined below. If you want to add a new flag, - in addition to the area below, you will also need to declare it in the - `DS.Model` class. - - - * [isEmpty](DS.Model.html#property_isEmpty) - * [isLoading](DS.Model.html#property_isLoading) - * [isLoaded](DS.Model.html#property_isLoaded) - * [isDirty](DS.Model.html#property_isDirty) - * [isSaving](DS.Model.html#property_isSaving) - * [isDeleted](DS.Model.html#property_isDeleted) - * [isNew](DS.Model.html#property_isNew) - * [isValid](DS.Model.html#property_isValid) - - @namespace DS - @class RootState - */ - - function hasDefinedProperties(object) { - // Ignore internal property defined by simulated `Ember.create`. - var names = Ember.keys(object); - var i, l, name; - for (i = 0, l = names.length; i < l; i++ ) { - name = names[i]; - if (object.hasOwnProperty(name) && object[name]) { return true; } - } - - return false; - } - - function didSetProperty(record, context) { - if (context.value === context.originalValue) { - delete record._attributes[context.name]; - record.send('propertyWasReset', context.name); - } else if (context.value !== context.oldValue) { - record.send('becomeDirty'); - } - - record.updateRecordArraysLater(); - } - - // Implementation notes: - // - // Each state has a boolean value for all of the following flags: - // - // * isLoaded: The record has a populated `data` property. When a - // record is loaded via `store.find`, `isLoaded` is false - // until the adapter sets it. When a record is created locally, - // its `isLoaded` property is always true. - // * isDirty: The record has local changes that have not yet been - // saved by the adapter. This includes records that have been - // created (but not yet saved) or deleted. - // * isSaving: The record has been committed, but - // the adapter has not yet acknowledged that the changes have - // been persisted to the backend. - // * isDeleted: The record was marked for deletion. When `isDeleted` - // is true and `isDirty` is true, the record is deleted locally - // but the deletion was not yet persisted. When `isSaving` is - // true, the change is in-flight. When both `isDirty` and - // `isSaving` are false, the change has persisted. - // * isError: The adapter reported that it was unable to save - // local changes to the backend. This may also result in the - // record having its `isValid` property become false if the - // adapter reported that server-side validations failed. - // * isNew: The record was created on the client and the adapter - // did not yet report that it was successfully saved. - // * isValid: The adapter did not report any server-side validation - // failures. - - // The dirty state is a abstract state whose functionality is - // shared between the `created` and `updated` states. - // - // The deleted state shares the `isDirty` flag with the - // subclasses of `DirtyState`, but with a very different - // implementation. - // - // Dirty states have three child states: - // - // `uncommitted`: the store has not yet handed off the record - // to be saved. - // `inFlight`: the store has handed off the record to be saved, - // but the adapter has not yet acknowledged success. - // `invalid`: the record has invalid information and cannot be - // send to the adapter yet. - var DirtyState = { - initialState: 'uncommitted', - - // FLAGS - isDirty: true, - - // SUBSTATES - - // When a record first becomes dirty, it is `uncommitted`. - // This means that there are local pending changes, but they - // have not yet begun to be saved, and are not invalid. - uncommitted: { - // EVENTS - didSetProperty: didSetProperty, - - propertyWasReset: function(record, name) { - var stillDirty = false; - - for (var prop in record._attributes) { - stillDirty = true; - break; - } - - if (!stillDirty) { record.send('rolledBack'); } - }, - - pushedData: Ember.K, - - becomeDirty: Ember.K, - - willCommit: function(record) { - record.transitionTo('inFlight'); - }, - - reloadRecord: function(record, resolve) { - resolve(get(record, 'store').reloadRecord(record)); - }, - - rolledBack: function(record) { - record.transitionTo('loaded.saved'); - }, - - becameInvalid: function(record) { - record.transitionTo('invalid'); - }, - - rollback: function(record) { - record.rollback(); - } - }, - - // Once a record has been handed off to the adapter to be - // saved, it is in the 'in flight' state. Changes to the - // record cannot be made during this window. - inFlight: { - // FLAGS - isSaving: true, - - // EVENTS - didSetProperty: didSetProperty, - becomeDirty: Ember.K, - pushedData: Ember.K, - - unloadRecord: function(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + " `", false); - }, - - // TODO: More robust semantics around save-while-in-flight - willCommit: Ember.K, - - didCommit: function(record) { - var dirtyType = get(this, 'dirtyType'); - - record.transitionTo('saved'); - record.send('invokeLifecycleCallbacks', dirtyType); - }, - - becameInvalid: function(record) { - record.transitionTo('invalid'); - record.send('invokeLifecycleCallbacks'); - }, - - becameError: function(record) { - record.transitionTo('uncommitted'); - record.triggerLater('becameError', record); - } - }, - - // A record is in the `invalid` if the adapter has indicated - // the the record failed server-side invalidations. - invalid: { - // FLAGS - isValid: false, - - // EVENTS - deleteRecord: function(record) { - record.transitionTo('deleted.uncommitted'); - record.clearRelationships(); - }, - - didSetProperty: function(record, context) { - get(record, 'errors').remove(context.name); - - didSetProperty(record, context); - }, - - becomeDirty: Ember.K, - - willCommit: function(record) { - get(record, 'errors').clear(); - record.transitionTo('inFlight'); - }, - - rolledBack: function(record) { - get(record, 'errors').clear(); - }, - - becameValid: function(record) { - record.transitionTo('uncommitted'); - }, - - invokeLifecycleCallbacks: function(record) { - record.triggerLater('becameInvalid', record); - }, - - exit: function(record) { - record._inFlightAttributes = {}; - } - } - }; - - // The created and updated states are created outside the state - // chart so we can reopen their substates and add mixins as - // necessary. - - function deepClone(object) { - var clone = {}, value; - - for (var prop in object) { - value = object[prop]; - if (value && typeof value === 'object') { - clone[prop] = deepClone(value); - } else { - clone[prop] = value; - } - } - - return clone; - } - - function mixin(original, hash) { - for (var prop in hash) { - original[prop] = hash[prop]; - } - - return original; - } - - function dirtyState(options) { - var newState = deepClone(DirtyState); - return mixin(newState, options); - } - - var createdState = dirtyState({ - dirtyType: 'created', - // FLAGS - isNew: true - }); - - createdState.uncommitted.rolledBack = function(record) { - record.transitionTo('deleted.saved'); - }; - - var updatedState = dirtyState({ - dirtyType: 'updated' - }); - - createdState.uncommitted.deleteRecord = function(record) { - record.clearRelationships(); - record.transitionTo('deleted.saved'); - }; - - createdState.uncommitted.rollback = function(record) { - DirtyState.uncommitted.rollback.apply(this, arguments); - record.transitionTo('deleted.saved'); - }; - - createdState.uncommitted.propertyWasReset = Ember.K; - - function assertAgainstUnloadRecord(record) { - Ember.assert("You can only unload a record which is not inFlight. `" + Ember.inspect(record) + "`", false); - } - - updatedState.inFlight.unloadRecord = assertAgainstUnloadRecord; - - updatedState.uncommitted.deleteRecord = function(record) { - record.transitionTo('deleted.uncommitted'); - record.clearRelationships(); - }; - - var RootState = { - // FLAGS - isEmpty: false, - isLoading: false, - isLoaded: false, - isDirty: false, - isSaving: false, - isDeleted: false, - isNew: false, - isValid: true, - - // DEFAULT EVENTS - - // Trying to roll back if you're not in the dirty state - // doesn't change your state. For example, if you're in the - // in-flight state, rolling back the record doesn't move - // you out of the in-flight state. - rolledBack: Ember.K, - unloadRecord: function(record) { - // clear relationships before moving to deleted state - // otherwise it fails - record.clearRelationships(); - record.transitionTo('deleted.saved'); - }, - - - propertyWasReset: Ember.K, - - // SUBSTATES - - // A record begins its lifecycle in the `empty` state. - // If its data will come from the adapter, it will - // transition into the `loading` state. Otherwise, if - // the record is being created on the client, it will - // transition into the `created` state. - empty: { - isEmpty: true, - - // EVENTS - loadingData: function(record, promise) { - record._loadingPromise = promise; - record.transitionTo('loading'); - }, - - loadedData: function(record) { - record.transitionTo('loaded.created.uncommitted'); - - record.suspendRelationshipObservers(function() { - record.notifyPropertyChange('data'); - }); - }, - - pushedData: function(record) { - record.transitionTo('loaded.saved'); - record.triggerLater('didLoad'); - } - }, - - // A record enters this state when the store asks - // the adapter for its data. It remains in this state - // until the adapter provides the requested data. - // - // Usually, this process is asynchronous, using an - // XHR to retrieve the data. - loading: { - // FLAGS - isLoading: true, - - exit: function(record) { - record._loadingPromise = null; - }, - - // EVENTS - pushedData: function(record) { - record.transitionTo('loaded.saved'); - record.triggerLater('didLoad'); - set(record, 'isError', false); - }, - - becameError: function(record) { - record.triggerLater('becameError', record); - }, - - notFound: function(record) { - record.transitionTo('empty'); - } - }, - - // A record enters this state when its data is populated. - // Most of a record's lifecycle is spent inside substates - // of the `loaded` state. - loaded: { - initialState: 'saved', - - // FLAGS - isLoaded: true, - - // SUBSTATES - - // If there are no local changes to a record, it remains - // in the `saved` state. - saved: { - setup: function(record) { - var attrs = record._attributes, - isDirty = false; - - for (var prop in attrs) { - if (attrs.hasOwnProperty(prop)) { - isDirty = true; - break; - } - } - - if (isDirty) { - record.adapterDidDirty(); - } - }, - - // EVENTS - didSetProperty: didSetProperty, - - pushedData: Ember.K, - - becomeDirty: function(record) { - record.transitionTo('updated.uncommitted'); - }, - - willCommit: function(record) { - record.transitionTo('updated.inFlight'); - }, - - reloadRecord: function(record, resolve) { - resolve(get(record, 'store').reloadRecord(record)); - }, - - deleteRecord: function(record) { - record.transitionTo('deleted.uncommitted'); - record.clearRelationships(); - }, - - unloadRecord: function(record) { - // clear relationships before moving to deleted state - // otherwise it fails - record.clearRelationships(); - record.transitionTo('deleted.saved'); - }, - - didCommit: function(record) { - record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType')); - }, - - // loaded.saved.notFound would be triggered by a failed - // `reload()` on an unchanged record - notFound: Ember.K - - }, - - // A record is in this state after it has been locally - // created but before the adapter has indicated that - // it has been saved. - created: createdState, - - // A record is in this state if it has already been - // saved to the server, but there are new local changes - // that have not yet been saved. - updated: updatedState - }, - - // A record is in this state if it was deleted from the store. - deleted: { - initialState: 'uncommitted', - dirtyType: 'deleted', - - // FLAGS - isDeleted: true, - isLoaded: true, - isDirty: true, - - // TRANSITIONS - setup: function(record) { - record.updateRecordArrays(); - }, - - // SUBSTATES - - // When a record is deleted, it enters the `start` - // state. It will exit this state when the record - // starts to commit. - uncommitted: { - - // EVENTS - - willCommit: function(record) { - record.transitionTo('inFlight'); - }, - - rollback: function(record) { - record.rollback(); - }, - - becomeDirty: Ember.K, - deleteRecord: Ember.K, - - rolledBack: function(record) { - record.transitionTo('loaded.saved'); - } - }, - - // After a record starts committing, but - // before the adapter indicates that the deletion - // has saved to the server, a record is in the - // `inFlight` substate of `deleted`. - inFlight: { - // FLAGS - isSaving: true, - - // EVENTS - - unloadRecord: assertAgainstUnloadRecord, - - // TODO: More robust semantics around save-while-in-flight - willCommit: Ember.K, - didCommit: function(record) { - record.transitionTo('saved'); - - record.send('invokeLifecycleCallbacks'); - }, - - becameError: function(record) { - record.transitionTo('uncommitted'); - record.triggerLater('becameError', record); - } - }, - - // Once the adapter indicates that the deletion has - // been saved, the record enters the `saved` substate - // of `deleted`. - saved: { - // FLAGS - isDirty: false, - - setup: function(record) { - var store = get(record, 'store'); - store.dematerializeRecord(record); - }, - - invokeLifecycleCallbacks: function(record) { - record.triggerLater('didDelete', record); - record.triggerLater('didCommit', record); - }, - - willCommit: Ember.K, - - didCommit: Ember.K - } - }, - - invokeLifecycleCallbacks: function(record, dirtyType) { - if (dirtyType === 'created') { - record.triggerLater('didCreate', record); - } else { - record.triggerLater('didUpdate', record); - } - - record.triggerLater('didCommit', record); - } - }; - - function wireState(object, parent, name) { - /*jshint proto:true*/ - // TODO: Use Object.create and copy instead - object = mixin(parent ? Ember.create(parent) : {}, object); - object.parentState = parent; - object.stateName = name; - - for (var prop in object) { - if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; } - if (typeof object[prop] === 'object') { - object[prop] = wireState(object[prop], object, name + "." + prop); - } - } - - return object; - } - - RootState = wireState(RootState, null, "root"); - - __exports__["default"] = RootState; - }); -define("ember-data/lib/system/record_array_manager", - ["./record_arrays","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var RecordArray = __dependency1__.RecordArray; - var FilteredRecordArray = __dependency1__.FilteredRecordArray; - var AdapterPopulatedRecordArray = __dependency1__.AdapterPopulatedRecordArray; - var ManyArray = __dependency1__.ManyArray; - var get = Ember.get; - var set = Ember.set; - var forEach = Ember.EnumerableUtils.forEach; - - /** - @class RecordArrayManager - @namespace DS - @private - @extends Ember.Object - */ - __exports__["default"] = Ember.Object.extend({ - init: function() { - this.filteredRecordArrays = Ember.MapWithDefault.create({ - defaultValue: function() { return []; } - }); - - this.changedRecords = []; - this._adapterPopulatedRecordArrays = []; - }, - - recordDidChange: function(record) { - if (this.changedRecords.push(record) !== 1) { return; } - - Ember.run.schedule('actions', this, this.updateRecordArrays); - }, - - recordArraysForRecord: function(record) { - record._recordArrays = record._recordArrays || Ember.OrderedSet.create(); - return record._recordArrays; - }, - - /** - This method is invoked whenever data is loaded into the store by the - adapter or updated by the adapter, or when a record has changed. - - It updates all record arrays that a record belongs to. - - To avoid thrashing, it only runs at most once per run loop. - - @method updateRecordArrays - @param {Class} type - @param {Number|String} clientId - */ - updateRecordArrays: function() { - forEach(this.changedRecords, function(record) { - if (get(record, 'isDeleted')) { - this._recordWasDeleted(record); - } else { - this._recordWasChanged(record); - } - }, this); - - this.changedRecords.length = 0; - }, - - _recordWasDeleted: function (record) { - var recordArrays = record._recordArrays; - - if (!recordArrays) { return; } - - forEach(recordArrays, function(array) { - array.removeRecord(record); - }); - }, - - _recordWasChanged: function (record) { - var type = record.constructor, - recordArrays = this.filteredRecordArrays.get(type), - filter; - - forEach(recordArrays, function(array) { - filter = get(array, 'filterFunction'); - this.updateRecordArray(array, filter, type, record); - }, this); - - // loop through all manyArrays containing an unloaded copy of this - // clientId and notify them that the record was loaded. - var manyArrays = record._loadingRecordArrays; - - if (manyArrays) { - for (var i=0, l=manyArrays.length; i [ { name: 'users', kind: 'hasMany' }, - // { name: 'owner', kind: 'belongsTo' } ] - relationships.get(App.Post); - //=> [ { name: 'posts', kind: 'hasMany' } ] - ``` - - @property relationships - @static - @type Ember.Map - @readOnly - */ - relationships: Ember.computed(function() { - var map = new Ember.MapWithDefault({ - defaultValue: function() { return []; } - }); - - // Loop through each computed property on the class - this.eachComputedProperty(function(name, meta) { - - // If the computed property is a relationship, add - // it to the map. - if (meta.isRelationship) { - meta.key = name; - var relationshipsForType = map.get(typeForRelationshipMeta(this.store, meta)); - - relationshipsForType.push({ name: name, kind: meta.kind }); - } - }); - - return map; - }).cacheable(false), - - /** - A hash containing lists of the model's relationships, grouped - by the relationship kind. For example, given a model with this - definition: - - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - var relationshipNames = Ember.get(App.Blog, 'relationshipNames'); - relationshipNames.hasMany; - //=> ['users', 'posts'] - relationshipNames.belongsTo; - //=> ['owner'] - ``` - - @property relationshipNames - @static - @type Object - @readOnly - */ - relationshipNames: Ember.computed(function() { - var names = { hasMany: [], belongsTo: [] }; - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - names[meta.kind].push(name); - } - }); - - return names; - }), - - /** - An array of types directly related to a model. Each type will be - included once, regardless of the number of relationships it has with - the model. - - For example, given a model with this definition: - - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - var relatedTypes = Ember.get(App.Blog, 'relatedTypes'); - //=> [ App.User, App.Post ] - ``` - - @property relatedTypes - @static - @type Ember.Array - @readOnly - */ - relatedTypes: Ember.computed(function() { - var type, - types = Ember.A(); - - // Loop through each computed property on the class, - // and create an array of the unique types involved - // in relationships - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - meta.key = name; - type = typeForRelationshipMeta(this.store, meta); - - Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type); - - if (!types.contains(type)) { - Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type); - types.push(type); - } - } - }); - - return types; - }).cacheable(false), - - /** - A map whose keys are the relationships of a model and whose values are - relationship descriptors. - - For example, given a model with this - definition: - - ```javascript - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post') - }); - ``` - - This property would contain the following: - - ```javascript - var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName'); - relationshipsByName.get('users'); - //=> { key: 'users', kind: 'hasMany', type: App.User } - relationshipsByName.get('owner'); - //=> { key: 'owner', kind: 'belongsTo', type: App.User } - ``` - - @property relationshipsByName - @static - @type Ember.Map - @readOnly - */ - relationshipsByName: Ember.computed(function() { - var map = Ember.Map.create(); - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - meta.key = name; - var relationship = relationshipFromMeta(this.store, meta); - relationship.type = typeForRelationshipMeta(this.store, meta); - map.set(name, relationship); - } - }); - - return map; - }).cacheable(false), - - /** - A map whose keys are the fields of the model and whose values are strings - describing the kind of the field. A model's fields are the union of all of its - attributes and relationships. - - For example: - - ```javascript - - App.Blog = DS.Model.extend({ - users: DS.hasMany('user'), - owner: DS.belongsTo('user'), - - posts: DS.hasMany('post'), - - title: DS.attr('string') - }); - - var fields = Ember.get(App.Blog, 'fields'); - fields.forEach(function(field, kind) { - console.log(field, kind); - }); - - // prints: - // users, hasMany - // owner, belongsTo - // posts, hasMany - // title, attribute - ``` - - @property fields - @static - @type Ember.Map - @readOnly - */ - fields: Ember.computed(function() { - var map = Ember.Map.create(); - - this.eachComputedProperty(function(name, meta) { - if (meta.isRelationship) { - map.set(name, meta.kind); - } else if (meta.isAttribute) { - map.set(name, 'attribute'); - } - }); - - return map; - }), - - /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. - - @method eachRelationship - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelationship: function(callback, binding) { - get(this, 'relationshipsByName').forEach(function(name, relationship) { - callback.call(binding, name, relationship); - }); - }, - - /** - Given a callback, iterates over each of the types related to a model, - invoking the callback with the related type's class. Each type will be - returned just once, regardless of how many different relationships it has - with a model. - - @method eachRelatedType - @static - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelatedType: function(callback, binding) { - get(this, 'relatedTypes').forEach(function(type) { - callback.call(binding, type); - }); - } - }); - - Model.reopen({ - /** - Given a callback, iterates over each of the relationships in the model, - invoking the callback with the name of each relationship and its relationship - descriptor. - - @method eachRelationship - @param {Function} callback the callback to invoke - @param {any} binding the value to which the callback's `this` should be bound - */ - eachRelationship: function(callback, binding) { - this.constructor.eachRelationship(callback, binding); - } - }); - }); -define("ember-data/lib/system/relationships/has_many", - ["../store","../relationship-meta","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /** - @module ember-data - */ - - var PromiseArray = __dependency1__.PromiseArray; - - var relationshipFromMeta = __dependency2__.relationshipFromMeta; - var typeForRelationshipMeta = __dependency2__.typeForRelationshipMeta; - - var get = Ember.get; - var set = Ember.set; - var setProperties = Ember.setProperties; - - function asyncHasMany(type, options, meta) { - return Ember.computed('data', function(key) { - var relationship = this._relationships[key], - promiseLabel = "DS: Async hasMany " + this + " : " + key; - - meta.key = key; - - if (!relationship) { - var resolver = Ember.RSVP.defer(promiseLabel); - relationship = buildRelationship(this, key, options, function(store, data) { - var link = data.links && data.links[key]; - var rel; - if (link) { - rel = store.findHasMany(this, link, relationshipFromMeta(store, meta), resolver); - } else { - rel = store.findMany(this, data[key], typeForRelationshipMeta(store, meta), resolver); - } - // cache the promise so we can use it - // when we come back and don't need to rebuild - // the relationship. - set(rel, 'promise', resolver.promise); - return rel; - }); - } - - var promise = relationship.get('promise').then(function() { - return relationship; - }, null, "DS: Async hasMany records received"); - - return PromiseArray.create({ - promise: promise - }); - }).meta(meta).readOnly(); - } - - function buildRelationship(record, key, options, callback) { - var rels = record._relationships; - - if (rels[key]) { return rels[key]; } - - var data = get(record, 'data'), - store = get(record, 'store'); - - var relationship = rels[key] = callback.call(record, store, data); - - return setProperties(relationship, { - owner: record, - name: key, - isPolymorphic: options.polymorphic - }); - } - - function hasRelationship(type, options) { - options = options || {}; - - var meta = { - type: type, - isRelationship: true, - options: options, - kind: 'hasMany', - key: null - }; - - if (options.async) { - return asyncHasMany(type, options, meta); - } - - return Ember.computed('data', function(key) { - return buildRelationship(this, key, options, function(store, data) { - var records = data[key]; - Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", Ember.A(records).isEvery('isEmpty', false)); - return store.findMany(this, data[key], typeForRelationshipMeta(store, meta)); - }); - }).meta(meta).readOnly(); - } - - /** - `DS.hasMany` is used to define One-To-Many and Many-To-Many - relationships on a [DS.Model](/api/data/classes/DS.Model.html). - - `DS.hasMany` takes an optional hash as a second parameter, currently - supported options are: - - - `async`: A boolean value used to explicitly declare this to be an async relationship. - - `inverse`: A string used to identify the inverse property on a related model. - - #### One-To-Many - To declare a one-to-many relationship between two models, use - `DS.belongsTo` in combination with `DS.hasMany`, like this: - - ```javascript - App.Post = DS.Model.extend({ - comments: DS.hasMany('comment') - }); - - App.Comment = DS.Model.extend({ - post: DS.belongsTo('post') - }); - ``` - - #### Many-To-Many - To declare a many-to-many relationship between two models, use - `DS.hasMany`: - - ```javascript - App.Post = DS.Model.extend({ - tags: DS.hasMany('tag') - }); - - App.Tag = DS.Model.extend({ - posts: DS.hasMany('post') - }); - ``` - - #### Explicit Inverses - - Ember Data will do its best to discover which relationships map to - one another. In the one-to-many code above, for example, Ember Data - can figure out that changing the `comments` relationship should update - the `post` relationship on the inverse because post is the only - relationship to that model. - - However, sometimes you may have multiple `belongsTo`/`hasManys` for the - same type. You can specify which property on the related model is - the inverse using `DS.hasMany`'s `inverse` option: - - ```javascript - var belongsTo = DS.belongsTo, - hasMany = DS.hasMany; - - App.Comment = DS.Model.extend({ - onePost: belongsTo('post'), - twoPost: belongsTo('post'), - redPost: belongsTo('post'), - bluePost: belongsTo('post') - }); - - App.Post = DS.Model.extend({ - comments: hasMany('comment', { - inverse: 'redPost' - }) - }); - ``` - - You can also specify an inverse on a `belongsTo`, which works how - you'd expect. - - @namespace - @method hasMany - @for DS - @param {String or DS.Model} type the model type of the relationship - @param {Object} options a hash of options - @return {Ember.computed} relationship - */ - function hasMany(type, options) { - if (typeof type === 'object') { - options = type; - type = undefined; - } - return hasRelationship(type, options); - } - - __exports__["default"] = hasMany; - }); -define("ember-data/lib/system/store", - ["./adapter","ember-inflector/lib/system/string","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - /*globals Ember*/ - /*jshint eqnull:true*/ - - /** - @module ember-data - */ - - var InvalidError = __dependency1__.InvalidError; - var Adapter = __dependency1__.Adapter; - var singularize = __dependency2__.singularize; - var get = Ember.get; - var set = Ember.set; - var once = Ember.run.once; - var isNone = Ember.isNone; - var forEach = Ember.EnumerableUtils.forEach; - var indexOf = Ember.EnumerableUtils.indexOf; - var map = Ember.EnumerableUtils.map; - var Promise = Ember.RSVP.Promise; - var copy = Ember.copy; - var Store, PromiseObject, PromiseArray, RecordArrayManager, Model; - - var camelize = Ember.String.camelize; - - // Implementors Note: - // - // The variables in this file are consistently named according to the following - // scheme: - // - // * +id+ means an identifier managed by an external source, provided inside - // the data provided by that source. These are always coerced to be strings - // before being used internally. - // * +clientId+ means a transient numerical identifier generated at runtime by - // the data store. It is important primarily because newly created objects may - // not yet have an externally generated id. - // * +reference+ means a record reference object, which holds metadata about a - // record, even if it has not yet been fully materialized. - // * +type+ means a subclass of DS.Model. - - // Used by the store to normalize IDs entering the store. Despite the fact - // that developers may provide IDs as numbers (e.g., `store.find(Person, 1)`), - // it is important that internally we use strings, since IDs may be serialized - // and lose type information. For example, Ember's router may put a record's - // ID into the URL, and if we later try to deserialize that URL and find the - // corresponding record, we will not know if it is a string or a number. - function coerceId(id) { - return id == null ? null : id+''; - } - - /** - The store contains all of the data for records loaded from the server. - It is also responsible for creating instances of `DS.Model` that wrap - the individual data for a record, so that they can be bound to in your - Handlebars templates. - - Define your application's store like this: - - ```javascript - MyApp.Store = DS.Store.extend(); - ``` - - Most Ember.js applications will only have a single `DS.Store` that is - automatically created by their `Ember.Application`. - - You can retrieve models from the store in several ways. To retrieve a record - for a specific id, use `DS.Store`'s `find()` method: - - ```javascript - var person = store.find('person', 123); - ``` - - If your application has multiple `DS.Store` instances (an unusual case), you can - specify which store should be used: - - ```javascript - var person = store.find('person', 123); - ``` - - By default, the store will talk to your backend using a standard - REST mechanism. You can customize how the store talks to your - backend by specifying a custom adapter: - - ```javascript - MyApp.store = DS.Store.create({ - adapter: 'MyApp.CustomAdapter' - }); - ``` - - You can learn more about writing a custom adapter by reading the `DS.Adapter` - documentation. - - ### Store createRecord() vs. push() vs. pushPayload() vs. update() - - The store provides multiple ways to create new records object. They have - some subtle differences in their use which are detailed below: - - [createRecord](#method_createRecord) is used for creating new - records on the client side. This will return a new record in the - `created.uncommitted` state. In order to persist this record to the - backend you will need to call `record.save()`. - - [push](#method_push) is used to notify Ember Data's store of new or - updated records that exist in the backend. This will return a record - in the `loaded.saved` state. The primary use-case for `store#push` is - to notify Ember Data about record updates that happen - outside of the normal adapter methods (for example - [SSE](http://dev.w3.org/html5/eventsource/) or [Web - Sockets](http://www.w3.org/TR/2009/WD-websockets-20091222/)). - - [pushPayload](#method_pushPayload) is a convenience wrapper for - `store#push` that will deserialize payloads if the - Serializer implements a `pushPayload` method. - - [update](#method_update) works like `push`, except it can handle - partial attributes without overwriting the existing record - properties. - - Note: When creating a new record using any of the above methods - Ember Data will update `DS.RecordArray`s such as those returned by - `store#all()`, `store#findAll()` or `store#filter()`. This means any - data bindings or computed properties that depend on the RecordArray - will automatically be synced to include the new or updated record - values. - - @class Store - @namespace DS - @extends Ember.Object - */ - Store = Ember.Object.extend({ - - /** - @method init - @private - */ - init: function() { - // internal bookkeeping; not observable - if (!RecordArrayManager) { RecordArrayManager = requireModule("ember-data/lib/system/record_array_manager")["default"]; } - this.typeMaps = {}; - this.recordArrayManager = RecordArrayManager.create({ - store: this - }); - this._relationshipChanges = {}; - this._pendingSave = []; - }, - - /** - The adapter to use to communicate to a backend server or other persistence layer. - - This can be specified as an instance, class, or string. - - If you want to specify `App.CustomAdapter` as a string, do: - - ```js - adapter: 'custom' - ``` - - @property adapter - @default DS.RESTAdapter - @type {DS.Adapter|String} - */ - adapter: '-rest', - - /** - Returns a JSON representation of the record using a custom - type-specific serializer, if one exists. - - The available options are: - - * `includeId`: `true` if the record's ID should be included in - the JSON representation - - @method serialize - @private - @param {DS.Model} record the record to serialize - @param {Object} options an options hash - */ - serialize: function(record, options) { - return this.serializerFor(record.constructor.typeKey).serialize(record, options); - }, - - /** - This property returns the adapter, after resolving a possible - string key. - - If the supplied `adapter` was a class, or a String property - path resolved to a class, this property will instantiate the - class. - - This property is cacheable, so the same instance of a specified - adapter class should be used for the lifetime of the store. - - @property defaultAdapter - @private - @return DS.Adapter - */ - defaultAdapter: Ember.computed('adapter', function() { - var adapter = get(this, 'adapter'); - - Ember.assert('You tried to set `adapter` property to an instance of `DS.Adapter`, where it should be a name or a factory', !(adapter instanceof Adapter)); - - if (typeof adapter === 'string') { - adapter = this.container.lookup('adapter:' + adapter) || this.container.lookup('adapter:application') || this.container.lookup('adapter:-rest'); - } - - if (DS.Adapter.detect(adapter)) { - adapter = adapter.create({ - container: this.container - }); - } - - return adapter; - }), - - // ..................... - // . CREATE NEW RECORD . - // ..................... - - /** - Create a new record in the current store. The properties passed - to this method are set on the newly created record. - - To create a new instance of `App.Post`: - - ```js - store.createRecord('post', { - title: "Rails is omakase" - }); - ``` - - @method createRecord - @param {String} type - @param {Object} properties a hash of properties to set on the - newly created record. - @return {DS.Model} record - */ - createRecord: function(typeName, inputPropoperties) { - var type = this.modelFor(typeName); - var properties = copy(inputPropoperties) || {}; - - // If the passed properties do not include a primary key, - // give the adapter an opportunity to generate one. Typically, - // client-side ID generators will use something like uuid.js - // to avoid conflicts. - - if (isNone(properties.id)) { - properties.id = this._generateId(type); - } - - // Coerce ID to a string - properties.id = coerceId(properties.id); - - var record = this.buildRecord(type, properties.id); - - // Move the record out of its initial `empty` state into - // the `loaded` state. - record.loadedData(); - - // Set the properties specified on the record. - record.setProperties(properties); - - return record; - }, - - /** - If possible, this method asks the adapter to generate an ID for - a newly created record. - - @method _generateId - @private - @param {String} type - @return {String} if the adapter can generate one, an ID - */ - _generateId: function(type) { - var adapter = this.adapterFor(type); - - if (adapter && adapter.generateIdForRecord) { - return adapter.generateIdForRecord(this); - } - - return null; - }, - - // ................. - // . DELETE RECORD . - // ................. - - /** - For symmetry, a record can be deleted via the store. - - Example - - ```javascript - var post = store.createRecord('post', { - title: "Rails is omakase" - }); - - store.deleteRecord(post); - ``` - - @method deleteRecord - @param {DS.Model} record - */ - deleteRecord: function(record) { - record.deleteRecord(); - }, - - /** - For symmetry, a record can be unloaded via the store. Only - non-dirty records can be unloaded. - - Example - - ```javascript - store.find('post', 1).then(function(post) { - store.unloadRecord(post); - }); - ``` - - @method unloadRecord - @param {DS.Model} record - */ - unloadRecord: function(record) { - record.unloadRecord(); - }, - - // ................ - // . FIND RECORDS . - // ................ - - /** - This is the main entry point into finding records. The first parameter to - this method is the model's name as a string. - - --- - - To find a record by ID, pass the `id` as the second parameter: - - ```javascript - store.find('person', 1); - ``` - - The `find` method will always return a **promise** that will be resolved - with the record. If the record was already in the store, the promise will - be resolved immediately. Otherwise, the store will ask the adapter's `find` - method to find the necessary data. - - The `find` method will always resolve its promise with the same object for - a given type and `id`. - - --- - - To find all records for a type, call `find` with no additional parameters: - - ```javascript - store.find('person'); - ``` - - This will ask the adapter's `findAll` method to find the records for the - given type, and return a promise that will be resolved once the server - returns the values. - - --- - - To find a record by a query, call `find` with a hash as the second - parameter: - - ```javascript - store.find('person', { page: 1 }); - ``` - - This will ask the adapter's `findQuery` method to find the records for - the query, and return a promise that will be resolved once the server - responds. - - @method find - @param {String or subclass of DS.Model} type - @param {Object|String|Integer|null} id - @return {Promise} promise - */ - find: function(type, id) { - Ember.assert("You need to pass a type to the store's find method", arguments.length >= 1); - Ember.assert("You may not pass `" + id + "` as id to the store's find method", arguments.length === 1 || !Ember.isNone(id)); - - if (arguments.length === 1) { - return this.findAll(type); - } - - // We are passed a query instead of an id. - if (Ember.typeOf(id) === 'object') { - return this.findQuery(type, id); - } - - return this.findById(type, coerceId(id)); - }, - - /** - This method returns a record for a given type and id combination. - - @method findById - @private - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {Promise} promise - */ - findById: function(typeName, id) { - var type = this.modelFor(typeName); - var record = this.recordForId(type, id); - var fetchedRecord = this.fetchRecord(record); - - return promiseObject(fetchedRecord || record, "DS: Store#findById " + type + " with id: " + id); - }, - - /** - This method makes a series of requests to the adapter's `find` method - and returns a promise that resolves once they are all loaded. - - @private - @method findByIds - @param {String} type - @param {Array} ids - @return {Promise} promise - */ - findByIds: function(type, ids) { - var store = this; - var promiseLabel = "DS: Store#findByIds " + type; - - return promiseArray(Ember.RSVP.all(map(ids, function(id) { - return store.findById(type, id); - })).then(Ember.A, null, "DS: Store#findByIds of " + type + " complete")); - }, - - /** - This method is called by `findById` if it discovers that a particular - type/id pair hasn't been loaded yet to kick off a request to the - adapter. - - @method fetchRecord - @private - @param {DS.Model} record - @return {Promise} promise - */ - fetchRecord: function(record) { - if (isNone(record)) { return null; } - if (record._loadingPromise) { return record._loadingPromise; } - if (!get(record, 'isEmpty')) { return null; } - - var type = record.constructor; - var id = get(record, 'id'); - var adapter = this.adapterFor(type); - - Ember.assert("You tried to find a record but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to find a record but your adapter (for " + type + ") does not implement 'find'", adapter.find); - - var promise = _find(adapter, this, type, id); - record.loadingData(promise); - return promise; - }, - - /** - Get a record by a given type and ID without triggering a fetch. - - This method will synchronously return the record if it is available in the store, - otherwise it will return `null`. A record is available if it has been fetched earlier, or - pushed manually into the store. - - _Note: This is an synchronous method and does not return a promise._ - - ```js - var post = store.getById('post', 1); - - post.get('id'); // 1 - ``` - - @method getById - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {DS.Model|null} record - */ - getById: function(type, id) { - if (this.hasRecordForId(type, id)) { - return this.recordForId(type, id); - } else { - return null; - } - }, - - /** - This method is called by the record's `reload` method. - - This method calls the adapter's `find` method, which returns a promise. When - **that** promise resolves, `reloadRecord` will resolve the promise returned - by the record's `reload`. - - @method reloadRecord - @private - @param {DS.Model} record - @return {Promise} promise - */ - reloadRecord: function(record) { - var type = record.constructor; - var adapter = this.adapterFor(type); - var id = get(record, 'id'); - - Ember.assert("You cannot reload a record without an ID", id); - Ember.assert("You tried to reload a record but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to reload a record but your adapter does not implement `find`", adapter.find); - - return _find(adapter, this, type, id); - }, - - /** - This method takes a list of records, groups the records by type, - converts the records into IDs, and then invokes the adapter's `findMany` - method. - - The records are grouped by type to invoke `findMany` on adapters - for each unique type in records. - - It is used both by a brand new relationship (via the `findMany` - method) or when the data underlying an existing relationship - changes. - - @method fetchMany - @private - @param {Array} records - @param {DS.Model} owner - @return {Promise} promise - */ - fetchMany: function(records, owner) { - if (!records.length) { - return Ember.RSVP.resolve(records); - } - - // Group By Type - var recordsByTypeMap = Ember.MapWithDefault.create({ - defaultValue: function() { return Ember.A(); } - }); - - forEach(records, function(record) { - recordsByTypeMap.get(record.constructor).push(record); - }); - - var promises = []; - - forEach(recordsByTypeMap, function(type, records) { - var ids = records.mapBy('id'), - adapter = this.adapterFor(type); - - Ember.assert("You tried to load many records but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to load many records but your adapter does not implement `findMany`", adapter.findMany); - - promises.push(_findMany(adapter, this, type, ids, owner)); - }, this); - - return Ember.RSVP.all(promises); - }, - - /** - Returns true if a record for a given type and ID is already loaded. - - @method hasRecordForId - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {Boolean} - */ - hasRecordForId: function(typeName, inputId) { - var type = this.modelFor(typeName); - var id = coerceId(inputId); - return !!this.typeMapFor(type).idToRecord[id]; - }, - - /** - Returns id record for a given type and ID. If one isn't already loaded, - it builds a new record and leaves it in the `empty` state. - - @method recordForId - @private - @param {String or subclass of DS.Model} type - @param {String|Integer} id - @return {DS.Model} record - */ - recordForId: function(typeName, inputId) { - var type = this.modelFor(typeName); - var id = coerceId(inputId); - var idToRecord = this.typeMapFor(type).idToRecord; - var record = idToRecord[id]; - - if (!record || !idToRecord.hasOwnProperty(id)) { - record = this.buildRecord(type, id); - } - - return record; - }, - - /** - @method findMany - @private - @param {DS.Model} owner - @param {Array} records - @param {String or subclass of DS.Model} type - @param {Resolver} resolver - @return {DS.ManyArray} records - */ - findMany: function(owner, inputRecords, typeName, resolver) { - var type = this.modelFor(typeName); - var records = Ember.A(inputRecords); - var unloadedRecords = records.filterBy('isEmpty', true); - var manyArray = this.recordArrayManager.createManyArray(type, records); - - forEach(unloadedRecords, function(record) { - record.loadingData(); - }); - - manyArray.loadingRecordsCount = unloadedRecords.length; - - if (unloadedRecords.length) { - forEach(unloadedRecords, function(record) { - this.recordArrayManager.registerWaitingRecordArray(record, manyArray); - }, this); - - resolver.resolve(this.fetchMany(unloadedRecords, owner)); - } else { - if (resolver) { resolver.resolve(); } - manyArray.set('isLoaded', true); - once(manyArray, 'trigger', 'didLoad'); - } - - return manyArray; - }, - - /** - If a relationship was originally populated by the adapter as a link - (as opposed to a list of IDs), this method is called when the - relationship is fetched. - - The link (which is usually a URL) is passed through unchanged, so the - adapter can make whatever request it wants. - - The usual use-case is for the server to register a URL as a link, and - then use that URL in the future to make a request for the relationship. - - @method findHasMany - @private - @param {DS.Model} owner - @param {any} link - @param {String or subclass of DS.Model} type - @return {Promise} promise - */ - findHasMany: function(owner, link, relationship, resolver) { - var adapter = this.adapterFor(owner.constructor); - - Ember.assert("You tried to load a hasMany relationship but you have no adapter (for " + owner.constructor + ")", adapter); - Ember.assert("You tried to load a hasMany relationship from a specified `link` in the original payload but your adapter does not implement `findHasMany`", adapter.findHasMany); - - var records = this.recordArrayManager.createManyArray(relationship.type, Ember.A([])); - resolver.resolve(_findHasMany(adapter, this, owner, link, relationship)); - return records; - }, - - /** - @method findBelongsTo - @private - @param {DS.Model} owner - @param {any} link - @param {Relationship} relationship - @return {Promise} promise - */ - findBelongsTo: function(owner, link, relationship) { - var adapter = this.adapterFor(owner.constructor); - - Ember.assert("You tried to load a belongsTo relationship but you have no adapter (for " + owner.constructor + ")", adapter); - Ember.assert("You tried to load a belongsTo relationship from a specified `link` in the original payload but your adapter does not implement `findBelongsTo`", adapter.findBelongsTo); - - return _findBelongsTo(adapter, this, owner, link, relationship); - }, - - /** - This method delegates a query to the adapter. This is the one place where - adapter-level semantics are exposed to the application. - - Exposing queries this way seems preferable to creating an abstract query - language for all server-side queries, and then require all adapters to - implement them. - - This method returns a promise, which is resolved with a `RecordArray` - once the server returns. - - @method findQuery - @private - @param {String or subclass of DS.Model} type - @param {any} query an opaque query to be used by the adapter - @return {Promise} promise - */ - findQuery: function(typeName, query) { - var type = this.modelFor(typeName); - var array = this.recordArrayManager - .createAdapterPopulatedRecordArray(type, query); - - var adapter = this.adapterFor(type); - - Ember.assert("You tried to load a query but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to load a query but your adapter does not implement `findQuery`", adapter.findQuery); - - return promiseArray(_findQuery(adapter, this, type, query, array)); - }, - - /** - This method returns an array of all records adapter can find. - It triggers the adapter's `findAll` method to give it an opportunity to populate - the array with records of that type. - - @method findAll - @private - @param {String or subclass of DS.Model} type - @return {DS.AdapterPopulatedRecordArray} - */ - findAll: function(typeName) { - var type = this.modelFor(typeName); - - return this.fetchAll(type, this.all(type)); - }, - - /** - @method fetchAll - @private - @param {DS.Model} type - @param {DS.RecordArray} array - @return {Promise} promise - */ - fetchAll: function(type, array) { - var adapter = this.adapterFor(type); - var sinceToken = this.typeMapFor(type).metadata.since; - - set(array, 'isUpdating', true); - - Ember.assert("You tried to load all records but you have no adapter (for " + type + ")", adapter); - Ember.assert("You tried to load all records but your adapter does not implement `findAll`", adapter.findAll); - - return promiseArray(_findAll(adapter, this, type, sinceToken)); - }, - - /** - @method didUpdateAll - @param {DS.Model} type - */ - didUpdateAll: function(type) { - var findAllCache = this.typeMapFor(type).findAllCache; - set(findAllCache, 'isUpdating', false); - }, - - /** - This method returns a filtered array that contains all of the known records - for a given type. - - Note that because it's just a filter, it will have any locally - created records of the type. - - Also note that multiple calls to `all` for a given type will always - return the same RecordArray. - - Example - - ```javascript - var localPosts = store.all('post'); - ``` - - @method all - @param {String or subclass of DS.Model} type - @return {DS.RecordArray} - */ - all: function(typeName) { - var type = this.modelFor(typeName); - var typeMap = this.typeMapFor(type); - var findAllCache = typeMap.findAllCache; - - if (findAllCache) { return findAllCache; } - - var array = this.recordArrayManager.createRecordArray(type); - - typeMap.findAllCache = array; - return array; - }, - - - /** - This method unloads all of the known records for a given type. - - ```javascript - store.unloadAll('post'); - ``` - - @method unloadAll - @param {String or subclass of DS.Model} type - */ - unloadAll: function(type) { - var modelType = this.modelFor(type); - var typeMap = this.typeMapFor(modelType); - var records = typeMap.records.slice(); - var record; - - for (var i = 0; i < records.length; i++) { - record = records[i]; - record.unloadRecord(); - record.destroy(); // maybe within unloadRecord - } - - typeMap.findAllCache = null; - }, - - /** - Takes a type and filter function, and returns a live RecordArray that - remains up to date as new records are loaded into the store or created - locally. - - The callback function takes a materialized record, and returns true - if the record should be included in the filter and false if it should - not. - - The filter function is called once on all records for the type when - it is created, and then once on each newly loaded or created record. - - If any of a record's properties change, or if it changes state, the - filter function will be invoked again to determine whether it should - still be in the array. - - Optionally you can pass a query which will be triggered at first. The - results returned by the server could then appear in the filter if they - match the filter function. - - Example - - ```javascript - store.filter('post', {unread: true}, function(post) { - return post.get('unread'); - }).then(function(unreadPosts) { - unreadPosts.get('length'); // 5 - var unreadPost = unreadPosts.objectAt(0); - unreadPost.set('unread', false); - unreadPosts.get('length'); // 4 - }); - ``` - - @method filter - @param {String or subclass of DS.Model} type - @param {Object} query optional query - @param {Function} filter - @return {DS.PromiseArray} - */ - filter: function(type, query, filter) { - var promise; - var length = arguments.length; - var array; - var hasQuery = length === 3; - - // allow an optional server query - if (hasQuery) { - promise = this.findQuery(type, query); - } else if (arguments.length === 2) { - filter = query; - } - - type = this.modelFor(type); - - if (hasQuery) { - array = this.recordArrayManager.createFilteredRecordArray(type, filter, query); - } else { - array = this.recordArrayManager.createFilteredRecordArray(type, filter); - } - - promise = promise || Promise.cast(array); - - - return promiseArray(promise.then(function() { - return array; - }, null, "DS: Store#filter of " + type)); - }, - - /** - This method returns if a certain record is already loaded - in the store. Use this function to know beforehand if a find() - will result in a request or that it will be a cache hit. - - Example - - ```javascript - store.recordIsLoaded('post', 1); // false - store.find('post', 1).then(function() { - store.recordIsLoaded('post', 1); // true - }); - ``` - - @method recordIsLoaded - @param {String or subclass of DS.Model} type - @param {string} id - @return {boolean} - */ - recordIsLoaded: function(type, id) { - if (!this.hasRecordForId(type, id)) { return false; } - return !get(this.recordForId(type, id), 'isEmpty'); - }, - - /** - This method returns the metadata for a specific type. - - @method metadataFor - @param {String or subclass of DS.Model} type - @return {object} - */ - metadataFor: function(type) { - type = this.modelFor(type); - return this.typeMapFor(type).metadata; - }, - - // ............ - // . UPDATING . - // ............ - - /** - If the adapter updates attributes or acknowledges creation - or deletion, the record will notify the store to update its - membership in any filters. - To avoid thrashing, this method is invoked only once per - - run loop per record. - - @method dataWasUpdated - @private - @param {Class} type - @param {DS.Model} record - */ - dataWasUpdated: function(type, record) { - this.recordArrayManager.recordDidChange(record); - }, - - // .............. - // . PERSISTING . - // .............. - - /** - This method is called by `record.save`, and gets passed a - resolver for the promise that `record.save` returns. - - It schedules saving to happen at the end of the run loop. - - @method scheduleSave - @private - @param {DS.Model} record - @param {Resolver} resolver - */ - scheduleSave: function(record, resolver) { - record.adapterWillCommit(); - this._pendingSave.push([record, resolver]); - once(this, 'flushPendingSave'); - }, - - /** - This method is called at the end of the run loop, and - flushes any records passed into `scheduleSave` - - @method flushPendingSave - @private - */ - flushPendingSave: function() { - var pending = this._pendingSave.slice(); - this._pendingSave = []; - - forEach(pending, function(tuple) { - var record = tuple[0], resolver = tuple[1]; - var adapter = this.adapterFor(record.constructor); - var operation; - - if (get(record, 'currentState.stateName') === 'root.deleted.saved') { - return resolver.resolve(record); - } else if (get(record, 'isNew')) { - operation = 'createRecord'; - } else if (get(record, 'isDeleted')) { - operation = 'deleteRecord'; - } else { - operation = 'updateRecord'; - } - - resolver.resolve(_commit(adapter, this, operation, record)); - }, this); - }, - - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is resolved. - - If the data provides a server-generated ID, it will - update the record and the store's indexes. - - @method didSaveRecord - @private - @param {DS.Model} record the in-flight record - @param {Object} data optional data (see above) - */ - didSaveRecord: function(record, data) { - if (data) { - // normalize relationship IDs into records - data = normalizeRelationships(this, record.constructor, data, record); - - this.updateId(record, data); - } - - record.adapterDidCommit(data); - }, - - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is rejected with a `DS.InvalidError`. - - @method recordWasInvalid - @private - @param {DS.Model} record - @param {Object} errors - */ - recordWasInvalid: function(record, errors) { - record.adapterDidInvalidate(errors); - }, - - /** - This method is called once the promise returned by an - adapter's `createRecord`, `updateRecord` or `deleteRecord` - is rejected (with anything other than a `DS.InvalidError`). - - @method recordWasError - @private - @param {DS.Model} record - */ - recordWasError: function(record) { - record.adapterDidError(); - }, - - /** - When an adapter's `createRecord`, `updateRecord` or `deleteRecord` - resolves with data, this method extracts the ID from the supplied - data. - - @method updateId - @private - @param {DS.Model} record - @param {Object} data - */ - updateId: function(record, data) { - var oldId = get(record, 'id'); - var id = coerceId(data.id); - - Ember.assert("An adapter cannot assign a new id to a record that already has an id. " + record + " had id: " + oldId + " and you tried to update it with " + id + ". This likely happened because your server returned data in response to a find or update that had a different id than the one you sent.", oldId === null || id === oldId); - - this.typeMapFor(record.constructor).idToRecord[id] = record; - - set(record, 'id', id); - }, - - /** - Returns a map of IDs to client IDs for a given type. - - @method typeMapFor - @private - @param type - @return {Object} typeMap - */ - typeMapFor: function(type) { - var typeMaps = get(this, 'typeMaps'); - var guid = Ember.guidFor(type); - var typeMap; - - typeMap = typeMaps[guid]; - - if (typeMap) { return typeMap; } - - typeMap = { - idToRecord: {}, - records: [], - metadata: {}, - type: type - }; - - typeMaps[guid] = typeMap; - - return typeMap; - }, - - // ................ - // . LOADING DATA . - // ................ - - /** - This internal method is used by `push`. - - @method _load - @private - @param {String or subclass of DS.Model} type - @param {Object} data - @param {Boolean} partial the data should be merged into - the existing data, not replace it. - */ - _load: function(type, data, partial) { - var id = coerceId(data.id); - var record = this.recordForId(type, id); - - record.setupData(data, partial); - this.recordArrayManager.recordDidChange(record); - - return record; - }, - - /** - Returns a model class for a particular key. Used by - methods that take a type key (like `find`, `createRecord`, - etc.) - - @method modelFor - @param {String or subclass of DS.Model} key - @return {subclass of DS.Model} - */ - modelFor: function(key) { - var factory; - - if (typeof key === 'string') { - var normalizedKey = this.container.normalize('model:' + key); - - factory = this.container.lookupFactory(normalizedKey); - if (!factory) { throw new Ember.Error("No model was found for '" + key + "'"); } - factory.typeKey = this._normalizeTypeKey(normalizedKey.split(':', 2)[1]); - } else { - // A factory already supplied. Ensure it has a normalized key. - factory = key; - if (factory.typeKey) { - factory.typeKey = this._normalizeTypeKey(factory.typeKey); - } - } - - factory.store = this; - return factory; - }, - - /** - Push some data for a given type into the store. - - This method expects normalized data: - - * The ID is a key named `id` (an ID is mandatory) - * The names of attributes are the ones you used in - your model's `DS.attr`s. - * Your relationships must be: - * represented as IDs or Arrays of IDs - * represented as model instances - * represented as URLs, under the `links` key - - For this model: - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr(), - lastName: DS.attr(), - - children: DS.hasMany('person') - }); - ``` - - To represent the children as IDs: - - ```js - { - id: 1, - firstName: "Tom", - lastName: "Dale", - children: [1, 2, 3] - } - ``` - - To represent the children relationship as a URL: - - ```js - { - id: 1, - firstName: "Tom", - lastName: "Dale", - links: { - children: "/people/1/children" - } - } - ``` - - If you're streaming data or implementing an adapter, - make sure that you have converted the incoming data - into this form. - - This method can be used both to push in brand new - records, as well as to update existing records. - - @method push - @param {String or subclass of DS.Model} type - @param {Object} data - @return {DS.Model} the record that was created or - updated. - */ - push: function(typeName, data, _partial) { - // _partial is an internal param used by `update`. - // If passed, it means that the data should be - // merged into the existing data, not replace it. - - Ember.assert("You must include an `id` for " + typeName+ " in a hash passed to `push`", data.id != null); - - var type = this.modelFor(typeName); - - // normalize relationship IDs into records - data = normalizeRelationships(this, type, data); - - this._load(type, data, _partial); - - return this.recordForId(type, data.id); - }, - - /** - Push some raw data into the store. - - This method can be used both to push in brand new - records, as well as to update existing records. You - can push in more than one type of object at once. - All objects should be in the format expected by the - serializer. - - ```js - App.ApplicationSerializer = DS.ActiveModelSerializer; - - var pushData = { - posts: [ - {id: 1, post_title: "Great post", comment_ids: [2]} - ], - comments: [ - {id: 2, comment_body: "Insightful comment"} - ] - } - - store.pushPayload(pushData); - ``` - - By default, the data will be deserialized using a default - serializer (the application serializer if it exists). - - Alternatively, `pushPayload` will accept a model type which - will determine which serializer will process the payload. - However, the serializer itself (processing this data via - `normalizePayload`) will not know which model it is - deserializing. - - ```js - App.ApplicationSerializer = DS.ActiveModelSerializer; - App.PostSerializer = DS.JSONSerializer; - store.pushPayload('comment', pushData); // Will use the ApplicationSerializer - store.pushPayload('post', pushData); // Will use the PostSerializer - ``` - - @method pushPayload - @param {String} type Optionally, a model used to determine which serializer will be used - @param {Object} payload - */ - pushPayload: function (type, inputPayload) { - var serializer; - var payload; - if (!inputPayload) { - payload = type; - serializer = defaultSerializer(this.container); - Ember.assert("You cannot use `store#pushPayload` without a type unless your default serializer defines `pushPayload`", serializer.pushPayload); - } else { - payload = inputPayload; - serializer = this.serializerFor(type); - } - serializer.pushPayload(this, payload); - }, - - /** - Update existing records in the store. Unlike [push](#method_push), - update will merge the new data properties with the existing - properties. This makes it safe to use with a subset of record - attributes. This method expects normalized data. - - `update` is useful if you app broadcasts partial updates to - records. - - ```js - App.Person = DS.Model.extend({ - firstName: DS.attr('string'), - lastName: DS.attr('string') - }); - - store.get('person', 1).then(function(tom) { - tom.get('firstName'); // Tom - tom.get('lastName'); // Dale - - var updateEvent = {id: 1, firstName: "TomHuda"}; - store.update('person', updateEvent); - - tom.get('firstName'); // TomHuda - tom.get('lastName'); // Dale - }); - ``` - - @method update - @param {String} type - @param {Object} data - @return {DS.Model} the record that was updated. - */ - update: function(type, data) { - Ember.assert("You must include an `id` for " + type + " in a hash passed to `update`", data.id != null); - - return this.push(type, data, true); - }, - - /** - If you have an Array of normalized data to push, - you can call `pushMany` with the Array, and it will - call `push` repeatedly for you. - - @method pushMany - @param {String or subclass of DS.Model} type - @param {Array} datas - @return {Array} - */ - pushMany: function(type, datas) { - return map(datas, function(data) { - return this.push(type, data); - }, this); - }, - - /** - If you have some metadata to set for a type - you can call `metaForType`. - - @method metaForType - @param {String or subclass of DS.Model} type - @param {Object} metadata - */ - metaForType: function(typeName, metadata) { - var type = this.modelFor(typeName); - - Ember.merge(this.typeMapFor(type).metadata, metadata); - }, - - /** - Build a brand new record for a given type, ID, and - initial data. - - @method buildRecord - @private - @param {subclass of DS.Model} type - @param {String} id - @param {Object} data - @return {DS.Model} record - */ - buildRecord: function(type, id, data) { - var typeMap = this.typeMapFor(type); - var idToRecord = typeMap.idToRecord; - - Ember.assert('The id ' + id + ' has already been used with another record of type ' + type.toString() + '.', !id || !idToRecord.hasOwnProperty(id)); - Ember.assert("`" + Ember.inspect(type)+ "` does not appear to be an ember-data model", (typeof type._create === 'function') ); - - // lookupFactory should really return an object that creates - // instances with the injections applied - var record = type._create({ - id: id, - store: this, - container: this.container - }); - - if (data) { - record.setupData(data); - } - - // if we're creating an item, this process will be done - // later, once the object has been persisted. - if (id) { - idToRecord[id] = record; - } - - typeMap.records.push(record); - - return record; - }, - - // ............... - // . DESTRUCTION . - // ............... - - /** - When a record is destroyed, this un-indexes it and - removes it from any record arrays so it can be GCed. - - @method dematerializeRecord - @private - @param {DS.Model} record - */ - dematerializeRecord: function(record) { - var type = record.constructor; - var typeMap = this.typeMapFor(type); - var id = get(record, 'id'); - - record.updateRecordArrays(); - - if (id) { - delete typeMap.idToRecord[id]; - } - - var loc = indexOf(typeMap.records, record); - typeMap.records.splice(loc, 1); - }, - - // ........................ - // . RELATIONSHIP CHANGES . - // ........................ - - addRelationshipChangeFor: function(childRecord, childKey, parentRecord, parentKey, change) { - var clientId = childRecord.clientId; - var parentClientId = parentRecord ? parentRecord : parentRecord; - var key = childKey + parentKey; - var changes = this._relationshipChanges; - if (!(clientId in changes)) { - changes[clientId] = {}; - } - if (!(parentClientId in changes[clientId])) { - changes[clientId][parentClientId] = {}; - } - if (!(key in changes[clientId][parentClientId])) { - changes[clientId][parentClientId][key] = {}; - } - changes[clientId][parentClientId][key][change.changeType] = change; - }, - - removeRelationshipChangeFor: function(clientRecord, childKey, parentRecord, parentKey, type) { - var clientId = clientRecord.clientId; - var parentClientId = parentRecord ? parentRecord.clientId : parentRecord; - var changes = this._relationshipChanges; - var key = childKey + parentKey; - if (!(clientId in changes) || !(parentClientId in changes[clientId]) || !(key in changes[clientId][parentClientId])){ - return; - } - delete changes[clientId][parentClientId][key][type]; - }, - - relationshipChangePairsFor: function(record){ - var toReturn = []; - - if( !record ) { return toReturn; } - - //TODO(Igor) What about the other side - var changesObject = this._relationshipChanges[record.clientId]; - for (var objKey in changesObject){ - if(changesObject.hasOwnProperty(objKey)){ - for (var changeKey in changesObject[objKey]){ - if(changesObject[objKey].hasOwnProperty(changeKey)){ - toReturn.push(changesObject[objKey][changeKey]); - } - } - } - } - return toReturn; - }, - - // ...................... - // . PER-TYPE ADAPTERS - // ...................... - - /** - Returns the adapter for a given type. - - @method adapterFor - @private - @param {subclass of DS.Model} type - @return DS.Adapter - */ - adapterFor: function(type) { - var container = this.container, adapter; - - if (container) { - adapter = container.lookup('adapter:' + type.typeKey) || container.lookup('adapter:application'); - } - - return adapter || get(this, 'defaultAdapter'); - }, - - // .............................. - // . RECORD CHANGE NOTIFICATION . - // .............................. - - /** - Returns an instance of the serializer for a given type. For - example, `serializerFor('person')` will return an instance of - `App.PersonSerializer`. - - If no `App.PersonSerializer` is found, this method will look - for an `App.ApplicationSerializer` (the default serializer for - your entire application). - - If no `App.ApplicationSerializer` is found, it will fall back - to an instance of `DS.JSONSerializer`. - - @method serializerFor - @private - @param {String} type the record to serialize - @return {DS.Serializer} - */ - serializerFor: function(type) { - type = this.modelFor(type); - var adapter = this.adapterFor(type); - - return serializerFor(this.container, type.typeKey, adapter && adapter.defaultSerializer); - }, - - willDestroy: function() { - var typeMaps = this.typeMaps; - var keys = Ember.keys(typeMaps); - var store = this; - - var types = map(keys, byType); - - this.recordArrayManager.destroy(); - - forEach(types, this.unloadAll, this); - - function byType(entry) { - return typeMaps[entry]['type']; - } - - }, - - /** - All typeKeys are camelCase internally. Changing this function may - require changes to other normalization hooks (such as typeForRoot). - - @method _normalizeTypeKey - @private - @param {String} type - @return {String} if the adapter can generate one, an ID - */ - _normalizeTypeKey: function(key) { - return camelize(singularize(key)); - } - }); - - function normalizeRelationships(store, type, data, record) { - type.eachRelationship(function(key, relationship) { - // A link (usually a URL) was already provided in - // normalized form - if (data.links && data.links[key]) { - if (record && relationship.options.async) { record._relationships[key] = null; } - return; - } - - var kind = relationship.kind, - value = data[key]; - - if (value == null) { return; } - - if (kind === 'belongsTo') { - deserializeRecordId(store, data, key, relationship, value, type); - } else if (kind === 'hasMany') { - deserializeRecordIds(store, data, key, relationship, value); - addUnsavedRecords(record, key, value); - } - }); - - return data; - } - - function deserializeRecordId(store, data, key, relationship, id, recordType) { - if (!Model) { Model = requireModule("ember-data/lib/system/model")["Model"]; } - if (isNone(id) || id instanceof Model) { - return; - } - - var type, inverse, record; - - if (typeof id === 'number' || typeof id === 'string') { - type = typeFor(relationship, key, data); - data[key] = store.recordForId(type, id); - } else if (typeof id === 'object') { - // polymorphic - data[key] = store.recordForId(id.type, id.id); - } - if(data[key] && recordType && data.id){ - if(window.debug) debugger; - record = store.recordForId(recordType, data.id); - if(data[key] && record){ - if(inverse = data[key].get(Ember.String.pluralize(recordType.typeKey)) ){ - inverse.addRecord(record); - } - } - } - } - - function typeFor(relationship, key, data) { - if (relationship.options.polymorphic) { - return data[key + "Type"]; - } else { - return relationship.type; - } - } - - function deserializeRecordIds(store, data, key, relationship, ids) { - for (var i=0, l=ids.length; i 'kine' - inflector.singularize('kine'); //=> 'cow' - ``` - - Creating an inflector and adding rules later. - - ```javascript - var inflector = Ember.Inflector.inflector; - - inflector.pluralize('advice'); // => 'advices' - inflector.uncountable('advice'); - inflector.pluralize('advice'); // => 'advice' - - inflector.pluralize('formula'); // => 'formulas' - inflector.irregular('formula', 'formulae'); - inflector.pluralize('formula'); // => 'formulae' - - // you would not need to add these as they are the default rules - inflector.plural(/$/, 's'); - inflector.singular(/s$/i, ''); - ``` - - Creating an inflector with a nondefault ruleset. - - ```javascript - var rules = { - plurals: [ /$/, 's' ], - singular: [ /\s$/, '' ], - irregularPairs: [ - [ 'cow', 'kine' ] - ], - uncountable: [ 'fish' ] - }; - - var inflector = new Ember.Inflector(rules); - ``` - - @class Inflector - @namespace Ember - */ - function Inflector(ruleSet) { - ruleSet = ruleSet || {}; - ruleSet.uncountable = ruleSet.uncountable || {}; - ruleSet.irregularPairs = ruleSet.irregularPairs || {}; - - var rules = this.rules = { - plurals: ruleSet.plurals || [], - singular: ruleSet.singular || [], - irregular: {}, - irregularInverse: {}, - uncountable: {} - }; - - loadUncountable(rules, ruleSet.uncountable); - loadIrregular(rules, ruleSet.irregularPairs); - } - - Inflector.prototype = { - /** - @method plural - @param {RegExp} regex - @param {String} string - */ - plural: function(regex, string) { - this.rules.plurals.push([regex, string.toLowerCase()]); - }, - - /** - @method singular - @param {RegExp} regex - @param {String} string - */ - singular: function(regex, string) { - this.rules.singular.push([regex, string.toLowerCase()]); - }, - - /** - @method uncountable - @param {String} regex - */ - uncountable: function(string) { - loadUncountable(this.rules, [string.toLowerCase()]); - }, - - /** - @method irregular - @param {String} singular - @param {String} plural - */ - irregular: function (singular, plural) { - loadIrregular(this.rules, [[singular, plural]]); - }, - - /** - @method pluralize - @param {String} word - */ - pluralize: function(word) { - return this.inflect(word, this.rules.plurals, this.rules.irregular); - }, - - /** - @method singularize - @param {String} word - */ - singularize: function(word) { - return this.inflect(word, this.rules.singular, this.rules.irregularInverse); - }, - - /** - @protected - - @method inflect - @param {String} word - @param {Object} typeRules - @param {Object} irregular - */ - inflect: function(word, typeRules, irregular) { - var inflection, substitution, result, lowercase, isBlank, - isUncountable, isIrregular, isIrregularInverse, rule; - - isBlank = BLANK_REGEX.test(word); - - if (isBlank) { - return word; - } - - lowercase = word.toLowerCase(); - - isUncountable = this.rules.uncountable[lowercase]; - - if (isUncountable) { - return word; - } - - isIrregular = irregular && irregular[lowercase]; - - if (isIrregular) { - return isIrregular; - } - - for (var i = typeRules.length, min = 0; i > min; i--) { - inflection = typeRules[i-1]; - rule = inflection[0]; - - if (rule.test(word)) { - break; - } - } - - inflection = inflection || []; - - rule = inflection[0]; - substitution = inflection[1]; - - result = word.replace(rule, substitution); - - return result; - } - }; - - __exports__["default"] = Inflector; - }); -define("ember-inflector/lib/system/string", - ["./inflector","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Inflector = __dependency1__["default"]; - var pluralize = function(word) { - return Inflector.inflector.pluralize(word); - }; - - var singularize = function(word) { - return Inflector.inflector.singularize(word); - }; - - __exports__.pluralize = pluralize; - __exports__.singularize = singularize; - }); -global.DS = requireModule('ember-data/lib/main')['default']; -}(Ember.lookup)); diff --git a/spec/javascript/models/product_test.coffee b/spec/javascript/models/product_test.coffee new file mode 100644 index 00000000..71f96110 --- /dev/null +++ b/spec/javascript/models/product_test.coffee @@ -0,0 +1,13 @@ +moduleForModel 'product', 'Product model', + needs: [ + 'model:product_variant', + 'model:product_category', + 'model:section_area', + 'model:product_order', + 'model:order', + 'model:supplier' + ] + +#test "Nothing yet", -> + #element = @subject() + #assert 2, 2 diff --git a/spec/javascript/models/section-element_test.coffee b/spec/javascript/models/section-element_test.coffee index 50300f54..19d108f4 100644 --- a/spec/javascript/models/section-element_test.coffee +++ b/spec/javascript/models/section-element_test.coffee @@ -1,5 +1,5 @@ moduleForModel 'section-element', 'Section element model', - needs: ['model:section', 'model:table'] + needs: ['model:section', 'model:table', 'model:section-area'] test "box_size", -> element = @subject diff --git a/wip.md b/wip.md index 944a7224..bf99750d 100644 --- a/wip.md +++ b/wip.md @@ -103,3 +103,11 @@ Integrations - Bonnetjes machine - Payleven? Payment + + +Braindump: + +Create blog +Explanation about ordering +The price is the actual price at the restaurant, is different when +waited for a long time