Filter json render in Rails - ruby-on-rails

What is the best way if i would like to only return :id and :name fields in JSON
So far i have:
format.json { render :json => #contacts.map(&:attributes) , :only => ["id"]}
But the "name" attribute does not work in the :only section, since it is not a column in the database (it is defined in the model as firstname + lastname)
Thanks!

Rails 3 supports following filter options. as simple as is
respond_to do |format|
format.json { render json: #contacts, :only => [:id, :name] }
end

You can pass :methods to to_json / as_json
format.json do
render :json => #contacts.map { |contact| contact.as_json(:only => :id, :methods => :name) }
end
Alternatively you can just build up a hash manually
format.json do
render :json => #contacts.map { |contact| {:id => contact.id, :name => contact.name} }
end
See: http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json

Related

RSS link in Rails

I need to add a link to a rss page but i don't know how
def index
#boxes = Box.paginate :page => params[:page], :order => "boxes.id desc", :per_page => 5,
:include => [:suppliers, :manufacturer]
#page_title = 'Catálogo'
respond_to do |format|
format.html
format.xml { render :xml => #boxes }
format.rss { render :layout => false }
end
end
the url to access is http://localhost:3000/catalog.rss but i don't know how to make it like this <%= link_to 'Canal RSS', :action => 'index' %>
In your routes.rb, you add the route first:
get 'catelog' => '<YOUR_CONTROLLER_NAME>#index', :constraints => {:format => :rss}
Then you can use it like this:
<%= link_to 'Canal RSS', catelog_url %>
catelog is the route prefix. You can run rake routes to see it.

Trying to have admin user create other users, but it keeps taking me to Edit User after submitting

I was following Tony Amoyal's setup for authentication using Devise/cancan and only allowing an admin to create/register new users. I didn't follow him exactly because there is no need for users to have multiple roles in this application, but for the most part I used exactly what he suggested.
It mostly works, but the biggest issue I am having right now is when I try to create a new user and submit it on the register form, it immediately complains, takes me to Devise's edit registration form and complains about the :current_password field not being filled in. If I fill anything in at that point, it will update MY user, not the one I was trying to register.
Any help to get it to actually create the user instead of requesting more changes would be appreciated.
#controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
before_filter :check_permissions, :only => [:new, :create, :cancel]
skip_before_filter :require_no_authentication
def check_permissions
authorize! :create, resource
end
end
#controllers/users_controller.rb
class UsersController < ApplicationController
load_and_authorize_resource :except =>[:create]
...
def new
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
end
...
def create
#user = User.new(params[:user])
if #user.save
respond_to do |format|
format.json { render :json => #user.to_json, :status => 200 }
format.xml { head :ok }
format.html { redirect_to :action => :index }
end
else
respond_to do |format|
format.json { render :text => "Could not create user", :status => :unprocessable_entity } # placeholder
format.xml { head :ok }
format.html { render :action => :new, :status => :unprocessable_entity }
end
end
end
end
#views/users/new.html.haml
= simple_form_for(#user, :method => :put, :html => { :class=>'form-horizontal' }) do |f|
%fieldset
%legend
= f.input :first_name
= f.input :last_name
= f.input :email
= f.input :password
= f.input :password_confirmation
.form-actions
= f.submit 'Register', :class => 'btn btn-primary'
= link_to 'Back', :back, :class => 'btn'
I was able to get around this problem by giving devise a path_prefix which would ensure this process is using my UsersController.

406 Not Acceptable on such a basic controller

Not sure why I'm getting this. I did a bunch of reading and I can't make heads or tails of this.
My controller:
def create
#emails = Email.new(params[:email])
respond_to do |format|
if #emails.save
flash[:notice] = 'Email was successfully created.'
format.html { redirect_to admin_emails_path(:mail_type => #emails.mail_type) }
format.xml { render :xml => #emails, :status => :created, :location => #emails }
else
format.html { render :action => "new" }
format.xml { render :xml => #emails.errors, :status => :unprocessable_entity }
end
end
end
Nothing crazy there. Its a multipart(images) form submission..maybe that has something to do with?
Update
Some irb stuff:
>> admin_emails_path(:mail_type => #emails.mail_type)
"/admin/emails?mail_type=magic_email"
>> admin_emails_path(#emails)
"/admin/emails.%23%3Cemail:0x109eb6360%3E"
The second example seems to be what it actually is returning, ignoring my additional params in the URL.
I should also note that my edit redirect is identical, and it works perfectly.
Update 2
Just to show how completely helpless this situation is, I've changed my controller to this :
if #emails.save
flash[:notice] = 'Email was successfully created.'
debugger
format.html { render :action => "new" } # <=== WTF ?
format.xml { render :xml => #emails, :status => :created, :location => #emails }
else
And I still get this:
Completed in 7401ms (View: 3, DB: 7) | 406 Not Acceptable [http://localhost/admin/emails.%23%3Cemail:0x109fd2a28%3E]
Routes
admin.resources :emails, :collection => {:test_email => :get}, :member => {:update_current => :get, :send_email => :get, :duplicate => :get} do |email|
email.resources :distributions, :collection => {:delete_dist => :get}
end
Form
- form_for #emails, :url => admin_email_path(#emails), :id => "email_form", :html => {:multipart => true} do |f|
... lots of stuff ..
.clear
%p
= f.submit 'Save Email', :class => "button"
The MIME type for the request is determined by the file extension incoming.
The error here is the following line:
>> admin_emails_path(#emails)
"/admin/emails.%23%3Cemail:0x109eb6360%3E"
The helper admin_emails_path should not be passed the list of e-mails. This collection path should work on it's own. When you pass in the #emails object, it's trying to encode it into the URL and injecting a period, which rails is parsing like a file extension (the url decoded version of %23%3Cemail:0x109eb6360%3E).
Change the reference from:
admin_emails_path(#emails)
to:
admin_emails_path
...and you will not see these format errors.

Rails 3 - Nested Forms and Default Values

I am trying to create form that contains another model in rails. I have accomplished this with using accepts_nested_attibutes and it is working great. The problem is I have an additional field in that table that records the User Name for each comment and I am not sure on how to insert that information when a new comment is being created. The username is being supplied by the Application Controller using the "current_user" method.
Regards,
Kyle
Comment Model
class Comment < ActiveRecord::Base
belongs_to :post
before_save :set_username
private
def set_username
self.created_by = current_user
end
end
Application Controller (This is just a Sandbox app so I just put a string in the method)
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
def current_user
"FName LName"
end
end
Show View
<p id="notice"><%= notice %></p>
<p>
<b>Title:</b>
<%= #post.title %>
</p>
<div id="show_comments"><%= render 'comments' %></div>
<div id="add_comments">
Add Comment
<%= form_for #post, :url => {:action => 'update', :id => #post.id}, :html => { :'data-type' => 'html', :id => 'create_comment_form' } do |f| %>
<%= f.fields_for :comments, #new_comment do |comment_fields| %>
<%= comment_fields.text_area :content %>
<%end%>
<div class="validation-error"></div>
<%= f.submit %>
<% end %>
</div>
Post Controller
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
#comments = #post.comments.all
format.html { redirect_to({:action => :show, :id => #post.id}, :notice => 'Post was successfully created.') }
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
I was originally thinking you could just set it as a default or a before_save in the model. But models don't have access to current_user. So it's probably best to just set the current user in the controller. It's not as DRY as putting it in the model but it's less hackey and potentially problematic this way.
def update
#post = Post.find(params[:id])
#post.attributes = params[:post]
#post.comments.each do |comment|
comment.created_by = current_user if comment.new_record?
end
respond_to do |format|
if #post.save
#comments = #post.comments.all
format.html { redirect_to({:action => :show, :id => #post.id}, :notice => 'Post was successfully created.') }
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
Just want to point out that it is possible to access current_user in the model scope. In this case I don think it is necessary, as the solution from #aNoble should work. So if is possible to set the current_user from the controller, I would prefer that.
In short, we add a method to the User class
class User < ActiveRecord::Base
cattr_accessor :current_user
def self.current_user
#current_user ||= User.new("dummy-user")
end
...
end
and in your application controllor, we add a before_filter that sets it. Make sure to call this filter after your authentication is done.
class ApplicationController < ActionController::Base
before_filter { |c| User.current_user = current_user }
end
And, then inside your Comment-model you could just do something like
class Comment
before_create :set_user
def set_user
created_by = User.current_user unless created_by
end
end
(so I only set the created_by if it was not yet set, and only upon creation of a new comment).

rails - Devise - Creating a Registration Form

I've been banging my head against the wall try to understand how to get Devise to work with customer registration....
So On my landing page I want to show a registration form, so I added this to my view:
<%= render 'devise/registrations/new' %>
In that partial I have in the view a form tag like follows:
<%= form_for(user_registration_path, :url => user_registration_path) do |f| %>
.
.
In my application layout I have:
<% flash.each do |key, value| %>
<div class="flash <%= key %>"><%= value %></div>
<% end %>
The issue I'm having is when I submit a new registration form with invalid params, I don't see the error message?
But if I submit valid info the form does say it worked and that I need to check my email for the confirmation link, which is good.
Can you help me understand how to get this working end-2-end so I can display the errors:
Here's my full controller:
# GET /users/new
# GET /users/new.xml
# GET /users/new.json HTML AND AJAX
#-------------------------------------------------------------------
def new
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
end
# GET /users/1/edit
# GET /users/1/edit.xml
# GET /users/1/edit.json HTML AND AJAX
#-------------------------------------------------------------------
def edit
respond_to do |format|
format.json { render :json => #user }
format.xml { render :xml => #user }
format.html
end
rescue ActiveRecord::RecordNotFound
respond_to_not_found(:json, :xml, :html)
end
# POST /users
# POST /users.xml
# POST /users.json HTML AND AJAX
#-----------------------------------------------------------------
def create
#user = User.new(params[:user])
if #user.save
respond_to do |format|
format.json { render :json => #user.to_json, :status => 200 }
format.xml { head :ok }
format.html { redirect_to :action => :index }
end
else
respond_to do |format|
format.json { render :text => "Could not create user", :status => :unprocessable_entity } # placeholder
format.xml { head :ok }
format.html { render :action => :new, :status => :unprocessable_entity }
end
end
end
The model:
validates :fname, :presence => true, :length => { :minimum => 2 }
validates :lname, :presence => true, :length => { :minimum => 2 }
validates :password, :presence => true, :length => { :minimum => 6 }
validates :email, :presence => true, :length => { :minimum => 6 }
From the github readme:
"Remember that Devise uses flash messages to let users know if sign in was successful or failed. Devise expects your application to call "flash[:notice]" and "flash[:alert]" as appropriate."
Where is your <%= error_messages_for %>? That is how errors will be displayed.
Also, devise needs virtual attributes in the model for additional attributes so don't forget to add :lname, :fname etc... to the devise user model or whatever you are doing.
Update
<%= f.error_messages_for :model %>
Before Rails 3 this is how errors were formatted, but in R3 it's depircated and you need to install a plugin to access errors this way. See comments for the link to the plugin
Just insert this at the beginning of your form, end before the end of it:
<%= devise_error_messages! %>
Devise is kinda tricky, I just finally managed to figure it out myself, and it's awesome. Btw, don't render the view as a partial, use templates, and then set your root to users#registrations_controller, given that you created custom controllers and views.
If you haven't, I wrote a blog post about it here

Resources