ember.js registerBoundHelper: creating an if-like helper that performs server-side authorization via json-Collection of common programming errors
javascript/coffeescript/ember.js/stackoverflow newbie here!
I’m trying to create an if-like helper that will display content only when the action on an object is authorized from the server. Basically I want the can? method from the rails cancan gem. I want to write the template something like this:
Viewing profile for {{email}}
Scores
{{#each score in scores}}
{{score.what}}
{{#can score action=destroy}}
Destroyable
{{/can}}
{{#can score action=view}}
Viewable
{{/can}}
{{#can score action=edit}}
Editable
{{/can}}
{{/each}}
The can ember.js helper will query the server with a json request that looks something like this:
/api/v1/authorization.json?action=destroy&cName=Score&id=1
The server would simply return the HTTP status codes 200 or 401, and ember would magically display or hide the content based on the status code. I have to do the authorization this way because there’s some role- & object based permissions checks that have to happen on the server, and I don’t want to duplicate the authorization logic in js.
So far, I’ve used the out-of-the-box if helper as an example to create the following custom bound helper (coffeescript).
Ember.Handlebars.registerBoundHelper 'can', (object, options) ->
permission = EmberDeviseExample.Authorization.create
action: options.hash.action
object: object
permission.authorize()
options.contexts = [permission]
Ember.Handlebars.helpers.boundIf.call(permission, "can", options)
Here’s the User object:
EmberDeviseExample.User = DS.Model.extend
email: DS.attr('string')
scores: DS.hasMany('EmberDeviseExample.Score')
EmberDeviseExample.store.adapter.serializer.map('EmberDeviseExample.User', {scores: {embedded: 'load'}})
And here’s the Authorization object:
EmberDeviseExample.Authorization = Ember.Object.extend
action: ''
object: null
response: 401
urlBase: ApiUrl.authorization_path
can : (->
return (this.get('response') == 200)
).property('response')
authorize: ->
# if object is an instance, include the id in the query params
# otherwise, just include the class name
obj = this.get('object')
cName = obj.toString()
id = null
if Ember.typeOf(obj) == "instance"
# cname looks something like ""
# turn it into "name"
cName = cName.split(':')[0].split('.')[1]
id = obj.get('id')
$.ajax
url : "#{this.get('urlBase')}.json"
context : this
type : 'GET'
data :
action : this.get('action')
cName : cName
id : id
complete : (data, textStatus, xhr) ->
this.set('response', data.status)
return this.get('can')
On the server side, the authorization.json code looks like this (rails 3). I am using the cancan gem to control authorization:
class AuthorizationsController < ApplicationController
respond_to :json
def show
id = params[:id]
cName = params[:cName]
action = params[:action]
object = cName.capitalize.constantize
object = object.find(id) unless id.blank?
authorized = can? action, object
render status: (authorized ? 200 : 401), json: {}
end
end
And the routes look like this:
scope "/api" do
scope "/v1" do
resource :authorization, only: [:show]
end
end
Of course there’s other code in my app, let me know if you need to see it.
When I remove the #can helpers, I see the rendered template. When I add the #can helpers back in, I get a javascript error, the message in Chrome is "Uncaught TypeError: Cannot read property '0' of null" @ ember.prod.js:2362.
Does anyone know how to build a helper that accepts an object and action, runs a server-side authorization check, and displays/hides content based on the server result?
Originally posted 2013-11-23 09:49:50.