I want to push to websockets what normally would be returned from a jbuilder view when the route is called but I cannot call render twice in an action:
def
...
#resource_receivers = "#{#resource.model_name.singular.capitalize}Receiver".constantize.where("#{#resource.model_name.singular}_id": #resource)
#resource_receivers.each do |resource_receiver|
resource_receiver.create_activity :sent, owner: #current_user, recipient: resource_receiver.receiver
end
to_return = render 'api/received/show'
Pusher.trigger('resources', 'resource.received', JSON.parse(to_return.first))
return render :task
end
How would I solve this? Or in general, I'd like to return what normally would be returned from the jbuilder anywhere else in my application, for example if I wanted to refactor this to have a method that pushes to the user
If you want to render a view and assign it to a variable instead of setting it as the response body, then you can use render_to_string.
Related
My question is : do we need a view file for each action in our controller?
(like if we defined a say_hello action in a controller, is it necessary to add say_hello.html.erb in his view directory?
I'll edit this to say it depends (with same content). If you plan on using that controller action as JS or JSON you don't need a view file. if you want one to share in multiple views, the file can contain a shared partial (which can be used in other views). This examples is shown by the generators scaffolding create examples like this. They are helpful if you are learning rails. Not great otherwise.
If you were to share a partial, you could have a partial named _form.html.erb and then inside your say_hello.html.erb file, it would just call:
<%= render 'form' %>
If you want to render JSON or JS files you can respond_to in your action:
respond_to do |format|
format.html # say_hello.html.erb
format.json { render json: #hello } #no file needed
format.js { render js: #hello }
#format.js {} #do nothing... or use a little javascript in there...
# or have a file named say_hello.js.erb and use your #hello variable
end
Edit:
One last update. Your say_hello.js.erb file can do the anything on another view (if called remotely):
say_hello.js.erb
<% if #hello.attribute == "some value" %>
$('#div_in_another_view').show();
<% else %>
$('#div_in_somewhere_else').hide();
<% end %>
You can do jQuery and anything you want to the view calling it (as long as it's using AJAX).
End edit
Guides are great place to get started. Railscasts.com as well (even though Ryan isn't updating anymore).
Edit: A great example on the different options on the respond_to is on this rails guide regarding javascript
You can just pass javascript straight from that format.js call, or use a file if you need more complicated stuff. You don't need to do anything also. You could just have it return xml or nothing as well, depending on your use case.
No, it is not required. For example, you can render json or xml data from the controller without needed a view at all. This article explains it very well http://guides.rubyonrails.org/layouts_and_rendering.html
No you do not need a view for each action. BUT you do need a view for each action that will reach the end of the method.
If you return anywhere in the action then you are fine. A view is only required when an implicit render is called due to execution reaching the end of the action.
No, it's really up to you and it depends on what the action will actually do. Actions can render different types content types: text, json, html, xml... etc. Here's an example:
def show
render xml: #something
end
This action doesn't have a view, but it'll output an xml when called. It can also render different things based on the format of the call:
def show
respond_to do |format|
format.html do
redirect_to '/'
end
format.json do
render xml: #something
end
end
end
The action may also redirect (again, this one doesn't have a file):
def show
redirect_to '/'
end
At the end, it's really up to the programmer to handle how the action behaves, but if you leave it empty, it'll assume there's a file to render.
I am writing a Ruby on Rails application with a controller called "pages_controller" that is responsible for displaying pages to users. There are 3 different types of pages that can be displayed, and different things have to happen on the back end in each case, so I decided to break the functionality out into 3 methods within the controller. When the user requests a page, the "show" method is called, which figures out whether the page:
1. Belongs to the user
2. Belongs to another user, and can be viewed by the user requesting it
3. Belongs to another user, and cannot be viewed by the user requesting it (unauthorized)
The appropriate method is then called from there to display the page. The code looks something like this:
def show
if (something)
showMine
elsif (something else)
showAnother
else
showUnauthorized
end
end
def showUnauthorized
respond_to do |format|
format.html # showUnauthorized.html.erb
end
end
def showMine
respond_to do |format|
format.html # showMine.html.erb
end
end
def showAnother
respond_to do |format|
format.html # showAnother.html.erb
end
end
I am getting a template missing error because rails wants to render a view when "show" is called, but I do not want any views to be rendered when "show" is called. I simply want "show" to call the correct method from there, and the corresponding view for that method (showMine, showAnother, or showUnauthorized) to be rendered. How can I do this? Or am I going about this the wrong way entirely?
You need to declare these new actions that you have created in the routes file, as they don't belong to the RESTful routes.
I sugest to keep only the show action in your controller and create the IFs in the show view using the render method to include the partials(_showMine.html.erb, showAnother.html.erb, showUnauthorized)
example:
show view:
if (something)
<%= render 'showMine' %>
elsif (something else)
<%= render 'showAnother' %>
else
<%= render 'showUnauthorized' %>
end
I hope it helps...
I basically agree with Samy's comment, but here's some background:
The method that tells Rails what view to use is render. If there's no call to that method in your show method, Rails assumes you have a view called show.xxx.xxx, e.g. show.html.erb, that is supposed to be rendered. Note that it doesn't assume template will be prefixed with show because that's the name of the method. It assumes it will be show because that's the name of the action. The name of the action is passed to the controller as part of the request; it's not simply derived from the name of whatever method has a respond_to block in it.
All the respond_to blocks do is specify different view templates based on the MIME type of the request, but since you never call render, all of those extra methods are still trying to call the show view (show.html.erb in every case), because you never told Rails to render any other view, and the action name is show.
So, instead of the respond_to blocks, just call render [some_view] in each of your other methods.
This might not be the clearest answer, but I'd suggest also reading the following:
http://ryanbigg.com/2009/04/how-rails-works-2-mime-types-respond_to/
It describes what respond_to does, in particular how it keys off the action name to determine what view to render.
I have a method called is_following in the user.rb model, and it checks to see if one user is following another.
Users Controller
def show
#user = User.find(params[:id])
respond_to |format|
format.json { render json: #user, :methods => [:image_url, :is_following] }
end
User Model
def is_following params
return "yep" if params[:follower_id] == id
end
However, I don't know how to pass params into the :is_following method in the controller. Anyone have any luck or solution to this?
This may not be the answer you're looking for but it definitely a solution. I recommend you stop using render to generate your JSON.
Gems such as jbuilder or rabl make it easier and gives you more flexibility.
Ruby on Rails is a MVC framework. You should leave view generation at the view layer. Doing this will save you from much more pain you'll endure in the future.
I am currently trying to add some parsing methods to a controller method in a Rails 3 application.
I have a controller action as follows:
def control
#device = Device.find(params[:id])
<do things>
parse_return(#returned_data)
end
and I added a custom method to the controller as below (this method would not have any routes and would only be accessible to controller actions):
def parse_return
<parse data>
end
but this does not appear to allow the parse_return method to be used. Is there somewhere else in the Rails app that I can put re-usable methods?
Thanks!
At a first glance it seems that you fail to render a response. Is it true that control action doesn't have an associated view?
In this case you have to manually call render in your action. For example, to render JSON response you can do this:
def control
# ...
render :json => parse_return(#returned_data),
:content_type => 'application/json',
:layout => false
end
You should include what the errors are.
What happens if you try this?
def parse_return(returned_data)
<parse data>
end
Perhaps the method is not expecting an parameter to be passed along with it.
Using the omniauth gem, I am forced to define a single route callback for succesful logins, regardless of the provider :
def auth_callback
auth_data = request.env['omniauth.auth']
if auth_data.has_key('something')
process_one(auth_data)
else
process_two(auth_data)
end
# No view is available here
end
def process_one
# do something then render view for process_one
return
end
def process_two
# do something then render view for process_two
return
end
How can I prevent the controller from returning to the auth_callback method and try to display the corresponding view (which does not exist) ? Treatment should be considered as complete once the process_one or process_two methods have returned.
Why not specifically call render in those methods?
def process_one
# do something then render view for process_one
render :process_one and return
end
Rails should detect that you've already run it and not try to render again.
If you want to return from the chain of methods, e.g.
def a
...
b
...
render "smth"
end
...
def b
...
# render from some conditional from here
...
end
will cause AbstractController::DoubleRenderError, which means that you call render twice.
You can read this article to find out 4 ways to manage such situation.