Rails 4 - Controller uses view path of super controller - ruby-on-rails

I have a SwitchesController that inherits from BaseSwitchesController, for the json.
module Api
module V1
class SwitchesController < BaseSwitchesController
respond_to :json
end
end
end
I have another SwitchesController, also inheriting from BaseSwitchesController, for the html.
class SwitchesController < BaseSwitchesController
layout 'application'
end
BaseSwitchesController inherits from ApplicationController.
class BaseSwitchesController < ApplicationController
def update
respond_to do |format|
if #switch.update(switch_params)
format.html { redirect_to #switch, notice: 'Switch was successfully updated.' }
format.json { render json: #switch.as_json.merge(:message => 'set_switch_value') }
else
format.html { render action: 'edit' }
format.json { render json: #switch.errors, status: :unprocessable_entity }
end
end
end
I want to move the json to the SwitchesController in the API module.
The problem is that the methods in SwitchesController are looking for views in views/base_switches, but that folder doesn't exist. It should be looking for views in views/switches, and choose e.g. index.json.rabl.
How can I fix this? Thanks!

I wouldn't personally mix together API controllers with HTML controllers.
I find two scenarios to be standard.
You do not have a traditional API (with it's own restful routes, versioning etc.) and you respond in both html and json. In which case you access the formats with a .format extension on the url or by using request headers. This uses the same views for both formats
You do have a separated API in which case you would not mix the views together and it allows you to use ActiveRecordSerializers, Rabl or whatever templating method you want to use.
Also your example goes against subclassing. Subclasses should be specializations of your superclass and should contain what differentiates them from other subclasses. Your superclass contains specialized behaviour from both subclasses. Since I'm assuming you don't want to respond to json outside of your API controller.
Hope this helps out

Related

Rails REST API and Views

I am creating a Rails app to which its users have two ways of interaction.
Through a web interface and through an API (mobile app and other software).
The functions for the web and the api access are the same, for example a user can write a comment via the web interface (views) or through the API.
What I would do now is create all the controllers with views, and then create a namespace /API/ with its own controllers. The problem now is of course that I have to write the function to write write a comment twice. Once in my PostController and once in my API/PostController.
I learned that Rails = DRY, so I guess I am doing something wrong.
How would I make the same functions available for my views and at the same time for my API (JSON response).
And how would the routes and namespaces look like? I think even if I find a way to not repeat myself it would be nice to have API routes like api/v1/...
I've done something similar, both using an API namespaced controller and using a single controller.
Since you seem convinced that you can do everything equally within your Web interface as your API interface it would make sense to merge the two, though note that this is not always true.
If it makes more sense for you to use a single controller, then what you need to do is play with the repond_to format of requests coming to a single controller action.
Ex:
class ArticlesController < ApplicationController
def index
#articles = Article.all
respond_to do |format|
format.html # will render a view by default
format.json { render json: #articles }
end
end
def create
article = current_user.articles.build(article_params)
respond_to do |format|
if article.save
flash[:notice] = "success!"
else
flash[:error] = "uhoh!"
end
format.html # renders a view by default
format.json { render json: { errors: #articles.errors }
end
end
private
def article_params
params.require(:article).permit(:title, :content)
end
end

rails - Auto create tasks after creating a project (after_create)

a newbie here. Just started to learn development. Any help would be greatly appreciated
I have two models Project and Task. Each project will have 7 tasks. I want rails to auto create my 7 tasks after I create a project.
My Task Controller
def create
#task = Task.new(task_params)
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: #task }
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
def task_params
params.require(:task).permit(:title, :description)
end
There are several ways you can do this.
1. Via callbacks
You can use callbacks in the Project model. Personally I do not recommend this approach since it this is not the intended use of callbacks, but it may work for you.
class Project < class Attachment < ActiveRecord::Base
after_create :create_tasks
private
def create_tasks
# Logic here to create the tasks. For example:
# tasks.create!(title: "Some task")
end
end
2. Nested attributes
You can build the child objects into the form and Rails will automatically create the child objects for you. Check out accepts_nested_attributes_for. This is more involved than using callbacks.
3. Use a form object
A form object can be a nice middle ground between callbacks and accepts_nested_attributes_for, but it raises the complexity a notch. Read up more about form objects here. There is also a nice Rails Casts episode on the topic, but it requires subscription.
There are other ways to do this as well, so it's up to you to find the right approach.
Another option would be to use Observer. This is more like callback.
But this is a great way to reduce the clutter that normally comes when the model class is burdened with functionality that doesn't pertain to the core responsibility of the class
class ProjectObserver < ActiveRecord::Observer
def after_create(project)
#add the logic to create tasks
end
end

show paperclip ( images) which stores in another app, current app already connect with the external database,Ruby on rails

I have a new app, and I want share the common part with another ror app.
For example, I have a MVC named showcase for showing images in the previous app, I want have the same one in my new app too.
Now I have connect my new app with the previous app's database, create a model with same name"showcase", and could get its columns like image_file_name; image_content_type;; image_file_size.. but in the view page for showcase in the new app, it can not recognize imagewhich is paperclip type attribute.
Can anyone tell me how can I use the images from the previous app?
Thanks a lot!
update:
class Showcase < ActiveResource::Base
self.site = "http://localhost:3000"
end
controller:
class ShowcasesController < ApplicationController
def index
#showcases = Showcase.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #showcases }
format.xml { render :xml => #showcases }
end
end
def show
#showcase = Showcase.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #showcase }
format.xml { render :xml => #showcases }
end
end
end
view:
<%= image_tag #showcase.image.url %
the error:
undefined method"image" for #
how could my view recognize image?
by the way in the original app, it didn't render xml, so I manually append render xml is that ok?
format.html # show.html.erb
format.json { render json: #showcase }
format.xml { render :xml => #showcases }
If you have a proper restful API to access the showcase resource in your first app(app1), then you can use the showcase model from the other ROR app(app2) ActiveResource. ActiveResource abstracts the communication between the two applications and it looks as though the model belonged to the same application.
If the restful url for showcase model in app1 is www.app1.com/showcases, then in the app2, all you have to do is create a model named showcase like below.
class ShowCase < ActiveResource:Base
self.site="www.app1.com"
end
Check out rails cast episode http://railscasts.com/episodes/94-activeresource-basics for more details.

How to render erb template to string inside action?

I need a string of html (something like "<html><body>Hello World</body></html>") for faxing purpose.
I wrote it into a seprate erb file: views/orders/_fax.html.erb ,
and try to render the erb in action: html_data = render(:partial => 'fax').
Here is part of the controller that raises the issue:
respond_to do |format|
if #order.save
html_data = render(:partial => 'fax')
response = fax_machine.send_fax(html_data)
......
format.html { redirect_to #order, notice: 'Order was successfully created.' }
format.json { render json: #order, status: :created, location: #order }
else
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
It gave me an AbstractController::DoubleRenderError as below:
AbstractController::DoubleRenderError in OrdersController#create
Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return".
How to solve this problem?
If you only need the rendered HTML, and don't need any functionality from the controller, you might try using ERB directly within a helper class, eg.:
module FaxHelper
def to_fax
html = File.open(path_to_template).read
template = ERB.new(html)
template.result
end
end
The ERB docs explain this in more detail.
EDIT
To get the instance variables from the controller, pass the binding into the result call, eg:
# controller
to_fax(binding)
# helper class
def to_fax(controller_binding)
html = File.open(path_to_template).read
template = ERB.new(html)
template.result(controller_binding)
end
Note: I've never done this, but it seems workable :)
Use the #render_to_string method
it works the same way as the typical render method but useful when you need to add some templated HTML to a json response
http://apidock.com/rails/ActionController/Base/render_to_string
If you don't want to escape html, just call .html_safe on it:
"<html><body>Hello World</body></html>".html_safe
Re your error, please post your OrdersController - looks like you are calling render or redirect more than once in the create action.
(Btw, just in case you are trying it - you can't render a partial in a controller - you can only render partials in views)
Edit: yeah your problem is you trying to render a partial in the controller action. You could use an after_create callback to set up and send the fax - though again you won't want to use a partial (as they are for views). http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
Edit: for your fax problem,you could create a normal Ruby Class, see this excellent bit of advice from Yehuda: https://stackoverflow.com/a/1071510/468009
The reason is you cannot render or redirect inside the same action more than once at a given time.
But in your code, you have both render and redirect. I think in your controller you can use simply only the render, assuming you don't need any json output.
Try this
def create
#order.save
render(:partial => 'fax')
end
I haven't tested this, but I guess you get the idea :), and think about a way to handle errors as well (in case order didn't save).

Abbreviating respond_to

I am currently working on a web application built on Rails 3 that heavily uses Ajax/REST for the client side. Thus, I often find myself writing controller actions like this:
def create
if !params[:name]
respond_to do |format|
format.html { render json: {}, status: :not_found }
format.json { render json: {}, status: :not_found }
end
return
end
account = ...
respond_to do |format|
format.html { render json: account }
format.json { render json: account }
end
end
Nearly all of my actions are returning a json object in a success case or an error code. However, I always have to write this verbose respond_to block and a return, if I want the action to return earlier.
Instead I would like to use something like this instead, or a similar alternative:
def create
if !params[:name]
throw :not_found
end
account = ...
return account
end
How can this be done with Rails 3+ ?
Have a look into inherited_resources. This will allow you to rewrite your controller as:
class SomeController < ApplicationController
inherit_resources
respond_to :html, :js, :json
end
That is it. All of your create/read/update/delete methods will be accessible as usual. You can, as I have in the past, inherit from a master resources controller which uses inherited_resources, and then you can tweak the responses in a more general way.
class ResourcesController < ApplicationController
inherit_resources
respond_to :html, :js
def create
create! do |format|
format.js do
# generic code here for managing all create methods initiated via js
# current model is avialbe via 'resource'
# e.g 'resource.errors'
end
end
end
Then simply inherit from that controller:
class SomeController < ResourcesController
end
This abstraction can be overkill for most purposes, but it has come in extremely handy when working 30 or 40 models which all require similar controllers.
Inherited_resources offers many helpers for accessing the current model (referred to as resource) to facilitate dynamic references, so you can, for example, return relevant forms, or partials based on resource/model name.
To give you an idea of how to use this, you could return forms for the current controller by using the controller name in the parameters. Should be noted that malformed controller names will not reach this method (as it will return 404), so it is safe to use:
format.js do
render "#{params[:controller]}/form"
end
Best of all, you can override any of the methods yourself by defining them in a particular controller.
If your are always returning json, you can ommit the respond_to block and write it like :
def create
if !params[:name]
render json: {}, status: :not_found
return
end
account = ...
render json: account
end

Resources