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

Automatic Ember Model Generation from Rails Serializers

Automatic Ember Model Generation from Rails Serializers


March 28, 2013

More Decks by mattrogish

Other Decks in Technology


  1. USER MODEL class User < ActiveRecord::Base attr_accessible :email, :first_name, :last_name,

    :phone, :birthdate belongs_to :organization def full_name "#{first_name} #{last_name}" end end Thursday, March 28, 13
  2. SERIALIZER class UserSerializer < ApplicationSerializer attributes :organization_id attributes :email, :first_name,

    :last_name, :phone has_one :organization # Computed attributes attributes :full_name, :ember_birthdate def ember_birthdate object.birthdate.strftime("%m/%d/%Y") end end Thursday, March 28, 13
  3. EMBER MODEL App.User = DS.Model.extend email: DS.attr('string') first_name: DS.attr('string') last_name:

    DS.attr('string') phone: DS.attr('string') full_name: DS.attr('string') ember_birthdate: DS.attr('string') organization: DS.belongsTo('App.Organization' Thursday, March 28, 13
  4. RAKE TASK namespace :db do namespace :schema do desc 'Regenerate

    the Ember schema.js based on the serializers' task :ember => :environment do schema_hash = {} Rails.application.eager_load! # populate descendants ApplicationSerializer.descendants.sort_by(&:name).each do | serializer_class| schema = serializer_class.schema schema_hash[serializer_class.model_class.name] = schema end schema_json = JSON.pretty_generate(schema_hash) File.open 'app/assets/javascripts/ember/models/schema.js', 'w' do |f| f << "// Model schema, auto-generated from serializers.\n" f << "// This file should be checked in like db/schema.rb.\n" f << "// Check lib/tasks/ember_schema.rake for documentation.\n" f << "window.serializerSchema = #{schema_json}\n" end end end end Thursday, March 28, 13
  5. USER SCHEMA window.serializerSchema = { "User": { "attributes": { "id":

    "integer", "organization_id": "integer", "email": "string", "first_name": "string", "last_name": "string", "full_name": "string", "ember_birthdate": "string", "phone": "string" }, "associations": { "organizations": { "belongs_to": "organization" } } } } Thursday, March 28, 13
  6. SCHEMA PARSER App.defineModelBaseClassesFromSchema = -> for className, schema of serializerSchema

    properties = {} for underscoredAttr, type of schema.attributes attr = underscoredAttr.camelize() if dsTypes[type]? if attr.match(/Id$/) and dsTypes[type] == 'number' # On the serializer side, we serialize belongs_to relationships as # integer _id fields, since AMS doesn't support belongs_to yet, and # has_one sideloads the association, causing infinite recursion. # Because of that, we infer a belongsTo relationship when we see _id # attributes in the schema. assoc = attr.replace(/Id$/, '') properties[assoc] = DS.belongsTo('App.' + assoc.capitalize()) else properties[attr] = DS.attr(dsTypes[type]) else # Ember.required doesn't quite do what we want it to yet, but maybe it # will be fixed. https://github.com/emberjs/ember.js/issues/1299 properties[attr] = Ember.required() for assoc, info of schema.associations assoc = assoc.camelize() if tableName = info?.belongs_to properties[assoc] = DS.belongsTo('App.' + tableName.classify().capitalize()) else if tableName = info?.has_many properties[assoc] = DS.hasMany('App.' + tableName.classify().capitalize()) else if tableName = info?.has_one properties[assoc] = DS.belongsTo('App.' + tableName.classify().capitalize()) # Do validator stuff here, if you so desire App["#{className}Base"] = App.Model.extend properties Thursday, March 28, 13
  7. DEFINITIONS #= require frontend/models/schema_parser App.Model = DS.Model.extend() # Define base

    classes like App.UserBase based on the schema, which in # turn is generated based on the serializers. Below, we only add server-side # associations, because the schema has their types as # `null`, as well as client-side computed properties. # # Check lib/tasks/ember_schema.rake for more documentation about the schema. App.defineModelBaseClassesFromSchema() App.User = App.UserBase.extend syncing: DS.attr('boolean') hasOrganizationBinding: 'organization.length' Thursday, March 28, 13