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

mattrogish

March 28, 2013
Tweet

More Decks by mattrogish

Other Decks in Technology

Transcript

  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