Devise + Backbone.js, cannot create new user - ruby-on-rails

Register view is rendered by backbone. When I submit the form to register a new user, I got the error like this.
I understand it's gonna check whether the email exists or not. But I don't know why I posted all my attributes but the backend seems not getting it.

I'd guess that your controller is looking at params[:user] to find the user's information but you don't have :user in your params. The manual talks about this:
Working with Rails
[...]
Similarly, Backbone PUTs and POSTs direct JSON representations of models, where by default Rails expects namespaced attributes. You can have your controllers filter attributes directly from params, or you can override toJSON in Backbone to add the extra wrapping Rails expects.
So adjust your controller to look at params rather than at something inside params or adjust your Backbone model's toJSON to look something like this:
toJSON: function() {
return { user: _.clone(this.attributes) };
}
If you change toJSON you'll want to adjust how you feed data to your templates to compensate for the toJSON return value change.

Related

Convert an instance to another type of instance

In ASP.NET, when I want to send a list of model instances to view layer, I convert them into another type (ModelView) by the following code:
var userViewModels = users.select(new {
Name = Name,
UserName = Username
});
I do this because I don't want to send all of my user model data (like password) to view layer. I put this code in my business logic Layer.
I'm using AJAX, and I'm sending my data by JSON protocol. What is the best practice in Ruby on Rails to do a similar action?
In RoR you simply pass models to your views.
Also, view in RoR can directly access (though it's not recommended) models from database. So "hiding" models does not make sense here.
You can consider rails generated scaffold as a close to the best practices.
Controller is used for selecting models from database
View is used for rendering model to the user
Model is the main place to store business logic
Although it is probably unnecessary, as dimakura pointed out, you can select arbitrary attributes when finding by using the select method. Your example would end up looking something like:
#users = User.select(:name, :username)

Backbone wants to include created_at, updated_at, etc., but I don't want it to

I'm using Backbone with Rails. I have a model that I can create and destroy just fine. When I edit, though, I get this error:
Can't mass-assign protected attributes: created_at, id, updated_at
That makes sense. Those attributes are protected and they should be protected. Backbone shouldn't be trying to update these attributes, but Backbone doesn't know better.
One option, of course, would be to remove params[:created_at], etc. in my Rails controller, but I can imagine that getting really un-DRY pretty quick, and plus it just seems wrong to have to do that.
Is there a way for me to tell Backbone not to include these attributes in its forms?
Either don't send them to the client so that your Backbone model never knows about them or override toJSON in your model to exclude them.
The default toJSON implementation is very simple:
toJSON: function() {
return _.clone(this.attributes);
}
so you can replace it with this:
toJSON: function() {
var attrs = _(this.attributes).clone();
delete attrs.created_at;
delete attrs.updated_at;
return attrs;
}
You could even monkey patch that right into Backbone.Model.prototype if that made sense to you.
The downside of altering toJSON is that toJSON tends to do double duty in Backbone:
toJSON is used to serialize models and collections for the server.
toJSON is used to serialize models and collections for views.
If you still want to use updated_at and created_at in views then I'd recommend adding another method, say serialize_for_view, that does what the standard toJSON does:
serialize_for_view: function() {
return _(this.attributes).clone();
}
and then use things like var html = this.template({m: this.model.serialize_for_view()}) to build your view's HTML. You could also monkey patch serialize_for_view into Backbone.Model.prototype if you wanted to use it everywhere.
I found that putting
model.unset("created_at");
model.unset("updated_at");
model.save()
fixed the problem. This won't work if you need those attributes, but if they are not needed, this works.

Passing JSON from Backbone.js to Rails 2

When Backbone.js passes data to the server, it doesn't nest the CGI params
in a model-name hash like Rails expects, so you have to pick them out of all
the other params via Hash.select or a reverse merge or some other hack in the controller action, or use
the emulateJSON hack in Backbone which JSON-encodes all the values in to a
single "model" parameter and decode them in the controller. Is there a more elegant solution on either side?
You can override toJSON() in each Backbone Model so that it gives itself a root element. For example:
toJSON: function() {
return {modelname: _.clone(this.attributes)}
}
It is also easy to generalize this in a model base class, perhaps checking for the existence of a Model's 'jsonRoot' property or similar.

ASP.NET MVC View Models and binding analogs in Rails

I have a good asp.net mvc background and I have recently started to learn Rails platform.
I'm trying to map my asp.net mvc experience to Rails concepts and want to ask you a question:
Does Rails have the same cool binding feature that allows me to use View Model to display a form and bind form data after submit to a View Model? I'm a bit confused, because I do understand that all Rails models are actually ActiveRecord's model, not a something like POCO objects, but can I do this in Rails? Can I have my custom model (non ActiveRecord) and bind form's data to this model after form's submit.
Thank you
You can if your custom model has an initializer function that accepts a hash as argument. That way you can create a form with the form_for tag and in the controller created a new object like so:
myObject = myObject.new(params[:myObject])
this will create a new object with the data from the hash, submitted by the form.
Ofcourse the initializer of your object needs to know which values are potentially available in the hash and extract them.
Your object could looks like this:
class MyObject
attr_accessor :prop1, :prop2, :prop3
def initialize(args = {})
self.prop1 = args[:prop1] if args.keys.contains(:prop1)
self.prop2 = args[:prop2] if args.keys.contains(:prop2)
self.prop3 = args[:prop3] if args.keys.contains(:prop3)
end
end
The above is a bit of pseudo code from ruby, but you should get the idea.

Scope of form variables in Grails

I am creating a Grails application which has an input page with text fields. Here the user can type in the data and on submit, the control goes to the action in controller.
Here I get the value of the form data using params.empName etc.
But the scope of this data is very small and doesnt get carried on if I do a redirect from the current action to another action.
Is there a way to increase the scope of the variables?
I am now to convert this to service oriented architecture. Therefore Is there a way to access these data in the service as well?
Please advice.
Thanks,
Megs
You can add...
params: params
...as an argument to the redirect, so that the incoming params are sent along with the redirect.
I don't think there's a built-in way to increase the scope. This is probably a Good Thing.
If you're redirecting in controllers, you should simply pass along the necessary parameters via the redirect() params dynamic property. Example:
def formHandler = {
// do stuff with params
redirect(action: 'anotherAction', params: params)
}
If you need scope to span multiple requests, e.g. if you're having a multi-step form entry given to the user, you might look into using web flows to persist state between requests.
For services, you're better off just passing down what you need as arguments to the service method, rather than exposing params. Example (similar to the Accessing Services section here):
// service
def myServiceMethod(def foo, def bar) {
// do stuff
}
// controller
def myService
def myControllerAction {
myService.myServiceMethod(params.foo, params.bar)
}
Exposing parameters from the controller to the service layer would break the layer-oriented approach Grails is trying to provide you; i.e. the "model" and "controller" components (of MVC) would be more tightly coupled.
I would also take a look at chaining actions as a way to pass the model information
http://www.grails.org/Controllers+-+Redirects

Resources