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