Question

Apologies, I have been in ember.js-land for so long, I'm fumbling Rails + active_model_serializers JSON api basics.

So let's say I'm a freelance journalist and am building an app where a writer can publish Articles to different Company-specific sites through a single user account.

Rails Relationships

User has_many :company_memberships
User has_many :companies, :through => :company_memberships

Company has_many :company_memberships

Company_membership belongs_to :user
Company_membership belongs_to :company

Article belongs_to :company_membership
Article has_many :comments

Comment belongs_to :article

I am using Rails 3+, Devise, Active_Model_Serializers, Ember-Data and Ember.js.

I want to set up a flow where the user comes to the site, logs in (very easy using devise) and is redirected to a rails page where he can select which Company's dashboard he wants to go to (by just clicking on a link).

Now comes the tricky part. When he clicks on a specific company's link, he should be directed to a Rails view that contains an ember app whose serialization scope is set to the current_company_membership or current_company and not just the current_user.

How do I create a rails or active_model_serializers method that scopes data to just the current_company as dictated by current_company_membership, and then passes along this data to the ember app?


Also, as a bonus, I need the name and email address of the current_user - how do I pass this information along to the ember app as well? I have seen this dude's article, but have not been able to get it working when trying to scope data through just company_membership and not current_user.

Any help would be awesome! I'll add more detail and code if need be, but I have a feeling I'm forgetting something pretty simple here.

Was it helpful?

Solution

Update: Way to access current_user and current_company rails methods in ember app described at bottom of answer and the github demo app has also been updated.


If I understand correctly what you're trying to do, below is one way to get the job done quickly. If anyone has a better answer, please edit my question. I have put up on github an ember + rails + devise demo app that combines the answer to this question and my last ember.js stackoverflow question to show that everything is working as it should be. Below I use the model Post instead of Article, but it's basically the same concept.

1) In the Rails Companies view, on specific company link_to click, you should pass the company variable to a rails controller action that's passed onward to an application helper method to set a devise session variable for current_company.

<% @companies.each do |company| %>
    <%= link_to 'Company Dashboard', set_current_company_company_path(:id => company) %>
<% end %>

class CompaniesController < ApplicationController

  def set_current_company
    session[:company_id] = params[:id]
    redirect_to assets_path 
  end
  ...
end

class ApplicationController < ActionController::Base
...
  helper_method :current_company

  def current_company
      @current_company ||= Company.find_by_id!(session[:company_id])
  end
...
end

App::Application.routes.draw do

resources :companies do
    member do
      get :set_current_company
    end
  end
...

The set_current_company method will set the current_company variable session-wide and redirect the user to the assets/index.html.erb rails view that contains your ember application.

2) You now need to scope your rails api data for Posts (and Comments, Company_Memberships) for current_company that you want to use in your json api/ember models, like so:

class PostsController < ApplicationController

 def index 
    @posts = Post.where(company_id: current_company.id)
    render json: @posts 
  end

def create
    @post = Post.new(params[:post].merge :company_id => current_company.id)

    respond_to do |format|
      if @post.save
         render json: @post, status: :created, location: @post
      else
    render json: @post.errors, status: :unprocessable_entity 
      end
    end
  end

3) Then you should serialize the data through AMS to the ember app in a normal way:

class PostSerializer < ActiveModel::Serializer
  attributes :id, :title, :body, :company_id, :user_id
  has_many :comments
end

4) Then on to the ember model.

App.Post = DS.Model.extend({
  title: DS.attr('string'),
  body: DS.attr('string'),
  comments: DS.hasMany('App.Comment')
});

5) Ember Controllers, Views and templates should behave as expected. Check out the demo app to see super basic implementations.


In order to gain access to current_user and current_company, use active_model_serializers meta data serialization and then this guy's temporary solution to map your serializer so that it grabs the meta data and sets it to a global variable in your ember app. This may not be best practices, but for now, it gets the job done.

1) First, set your store.js grab the meta data from your json and set it to a global variable in your serializer:

App.CustomRESTSerializer = DS.RESTSerializer.extend({
  extractMeta: function(loader, type, json) {
    var meta;
    meta = json[this.configOption(type, 'meta')];
    if (!meta) { return; }
    Ember.set('App.metaData', meta);
    this._super(loader, type, json);
  }
});

App.Store = DS.Store.extend({
  revision: 11,
  adapter:  DS.RESTAdapter.create({
    bulkCommit: false,
    serializer: App.CustomRESTSerializer
    }),
});

App.ready = function() {
  App.CompanyMembership.find();
}

2) Second, in your rails company_memberships_controller.rb render the meta data:

render json: @company_memberships, :meta => {:current_user => current_user, :current_company => current_company}

3) Finally, call any current_user or current_company attribute straight from your templates:

Logged in with: {{App.metaData.current_user.email}}
<br>
Current Company: {{App.metaData.current_company.name}}

OTHER TIPS

Simply override the methods in the serializer.

def custom_association
  # scope is the current user
  CustomObjects.ownedBy(scope)
end

I'm not sure if I understand your problem completely, but:

You can set the serialization scope in your controller class definition (or on the ApplicationController base class):

serialization_scope :current_company

You can also pass a scope to the serializer when you instantiate it. So to return the JSON for record, you'd do this, in your controller method:

record.active_model_serializer.new(record, scope: current_company).as_json
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top