I want to save json into a database field for later use. How should I go about that with Jbuilder?
I want to use an item's show template, passing in the object, #item to save the output for that Item into the database for later use.
I got some output with the following code:
view_paths = Rails::Application::Configuration.new(Rails.root).paths["app/views"]
av_helper = ActionView::Base.new view_paths
include Rails.application.routes.url_helpers
#job = Job.find(239)
output = av_helper.render(file: '/api/jobs/show.jbuilder', locals: {:#job => #job})
How can I render the saved json directly from the controller?
Add this for the action code in the controller
def show
#job = Job.find(params[:id])
render :inline => #job.json_output
end
render_to_string
Raw rendering of a template to a string.
It is similar to render, except that it does not set the response_body
and it should be guaranteed to always return a string.
Also if you're doing it from a controller there you can just use the controller to render it:
class JobsController
def create
#job = Job.new(item_params) do |job|
job.my_json_attribute = render_to_string(:show, locals: { :#job => job})
end
if #job.save
redirect_to #job
else
render :new
end
end
end
But this seems like a pretty overcomplicated and flawed way to handle something that can be done with e-tag caching and a reverse proxy or even low level caching. Especially since you would have to repeat the logic when updating the item.
Related
I have a helper which instantiates a model and renders a form. This form should be available to any view in the application
# support_form_helper
def support_form
#support_stats = SupportForm::Stat.find(get_stats_id)
#enquiry = SupportForm::Enquiry.new(stats_id: #support_stats.id)
render partial: 'support_form/enquiries/form'
end
And its rendered in the view:
# some_view.html.erb
<%= support_form %>
This is fine until I want to submit the form and validate it in the controller.
# enquiries_controller.rb
def create
#enquiry = SupportForm::Enquiry.new(params[:support_form_enquiry])
topic = #enquiry.topic
#stat = SupportForm::Stat.find(#enquiry.stats_id)
#stat.stats[topic] = #stat.stats[topic].to_i.next
respond_to do |format|
if #enquiry.valid? && #stat.save
format.html { redirect_to(root_path) }
else
format.html { redirect_to(:back) }
end
end
end
This is where I can't render the previous view with the errors attached to the invalid object. The helper gets invoked again and initializes a new #enquiries object, without the errors obviously.
How can I render the form in many views across an application and still return to the view with the object together with errors when it is invalid?
I found an answer which answers my question but its a bad idea:
Render the action that initiated update
def create
#enquiry = SupportForm::Enquiry.new(params[:support_form_enquiry])
topic = #enquiry.topic
#stat = SupportForm::Stat.find(#enquiry.stats_id)
#stat.stats[topic] = #stat.stats[topic].to_i.next
if #enquiry.valid? && #stat.save
redirect_to(root_path)
else
render Rails.application.routes.recognize_path(request.referer).values.join("/")
end
end
The problem is that there will likely be instance variables in the view that submitted the form and I would have to be able to instantiate all the instance variable in the application then.....not possible.
Currently I'm considering putting the errors in the flash hash... not something I want to do. With the original object returned i can repopulate the fields with the users input.
When you use redirect_to, rails will kick off a whole new controller & view sequence. Use
render "path/to/template/from/view/folder"`
instead.
A typical create action using this pattern would look like (for a 'post' object in this case):
def create
#post = Post.new(params[:post])
#created = #post.save
respond_to do |format|
if #created
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to post_path(#post) }
format.js
else
format.html { render :action => :new }
format.js
end
end
end
Notice how if it's successfully created we do a full redirect to the "show" page for the post, but if it's not successful we just do a render.
You should probably modify your support_form helper so that it only creates a new #enquiry if it hasn't been created already:
def support_form
#support_stats = SupportForm::Stat.find(get_stats_id)
#enquiry ||= SupportForm::Enquiry.new(stats_id: #support_stats.id)
render partial: 'support_form/enquiries/form'
end
||= is shorthand for "equals itself or". If it hasn't been defined (or is nil or false) then it will fail the first part of the or and pass through to the second, where the object is created.
In your form partial, also, you should make sure you're using form_for, which will submit to the create or update action depending on whether the object has been saved already.
When I initially call the new function all variables load up correctly. The params[:code] is a URL param defined in routes. However, when validation fails on create and new is rendered, the #m variable is not loaded (this causes a nomethoderror when an attribute of #m is called in the 'new' template). So, the :code parameter is not obtained after rendering new. However can the :code parameter be preserved after validation fails?
class AffiliatesController < ApplicationController
def new
#m = Merchant.find_by_code(params[:code])
#affiliate = Affiliate.new
end
def create
a = Affiliate.new(params[:affiliate])
if a.save
redirect_to 'http://' + params[:ref]
else
render 'new'
end
end
end
Another way to preserve params[:code], aside from using the session, is to add a hidden field in the form.
<%= form_for #affiliate do |f| %>
<%= hidden_field_tag :code, #m.code %>
Then change the create action to
def create
#affiliate = Affiliate.new(params[:affiliate])
if #affiliate.save
redirect_to 'http://' + params[:ref]
else
#m = Merchant.find_by_code(params[:code])
render :new
end
end
before you call render you should fill in all the variables that will be used by the view.
So in your case you need to instantiate #m = ... before you render 'new' from within the create as well.
If you need an extra parameter to accomplish this (the param[:code] in your case) I would not recomment you to configure routes and pass this information over the URI, it's complicated.
Use the session instead, it's a lot easier!
For example:
in index (or wherever you have the merchant code available) add session[:merchant_code] = the_code.
in new change #m = Merchant.find_by_code(session[:merchant_code])
Cheers,
I want to output a list of affiliate links, each tagged to identify the current user. It would be simple in HTML, but we're writing an API, so the output is JSON.
I have it working, but it seems overly complicated. Is this the best approach?
My model, AffiliateLink contains a field (the raw HTML of the link) that I'll transform and output on the fly by adding a token. I have a model method that produces the replacement -- it is non-trivial because we use multiple affiliates and each has a special transformation rule that this method knows about:
def link_with_token(user_token)
# some gnarly code that depends on a lot of stuff the model knows
# that returns a proper link
end
To get my correct link html in JSON I have done these things:
add attr_accessor :link_html to model
add an instance method to set the new accessor
...
def set_link_html(token)
self.link_html = link_with_tracking_token(token)
end
override as_json in the model, replacing the original html_code with link_html
...
def as_json(options = {})
super(:methods => :link_html, :except => :html_code)
end
iterate over the collection returned in the controller method to do the transformation
...
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
#links.each do |link|
link.set_link_html(account_tracking_token)
end
render json: #links
end
end
end
This seems like a lot of stuff to do just to get my teensy-weensy transformation done. Helpful suggestions (relating to this problem and not to other aspects of the code, which is in flux now) are welcome.
1) A quick solution to your problem (as demonstrated here):
affiliate_links_controller.rb
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
render json: #links.to_json(:account_tracking_token => account_tracking_token)
end
end
end
AffiliateLink.rb
# I advocate reverse_merge so passed-in options overwrite defaults when option
# keys match.
def as_json(options = {})
json = super(options.reverse_merge(:except => :html_code))
json[:link_with_token] = link_with_token(options[:account_tracking_token])
json
end
2) A more hardcore solution, if you're really writing an API:
See this article describing your problem.
See the gem that the authors made as a solution.
See this railscast on using the gem.
3) And lastly, the convenient solution. If you have a convenient model relation, this is clean:
Pretending AffiliateLink belongs_to :user. And assuming user_token is an accessible attribute of User.
AffiliateLink.rb
# have access to user.user_token via relation
def link_with_token
# some gnarly code that depends on a lot of stuff the model knows
# that returns a proper link
end
def as_json(options = {})
super(options.reverse_merge(:methods => :link_with_token, :except => :html_code))
end
affiliate_links_controller.rb
def index
#links = Admin::AffiliateLink.all # TODO, pagination, etc.
respond_to do |format|
format.html # index.html.erb
format.json do
render json: #links
end
end
end
In most of the examples I see, I notice that in the controller, the new method simply has:
#object = Object.new
In the create method, you'll see something like:
#object = Object.find(params[:object])
if #object.save
flash[:success] = "This object has been added."
redirect_to objects_path
else
render :action => 'new'
end
This works fine, but the only thing I run into (pretty common), is when the validations fail using some built in helper methods:
validates_presence_of :value
the "render :action => 'new' will be called. So then rails bypasses the controller and goes straight to the action for new, which attempts to render the form.
That bypass of the controller kills me, because sometimes I'm loading values into the form using values I've defined in my controller.
I'll end up getting errors because of nil values, because rails bypassed the controller and the values were never set when loading "render :action => 'new'".
My question: What's the best way to redirect a form (or best practice in general) when validating a form that has variables assigned in it that is defined the in controller? I want to avoid those nil value errors, so I'm thinking I'm just doing it wrong to begin with.
You could move your custom bit of code that loads your various values into a before_filter, a bit like:
before_filter :get_values, :only => [:new, :create]
def new
# your code here
end
def create
#object = Object.new params[:object
if #object.save
flash[:success] = "This object has been added."
redirect_to objects_path
else
render :action => 'new'
end
end
private
def get_values
# your code here
end
One way: render hidden fields for all those variables, so your object will already have them in create action.
Another way: create a before_filter for those two actions.
Personally I'd go with the second option.
Until now I have always specified the format of the response for actions using a responds_to block, like so:
responds_to do |format|
format.js { render :json => #record }
end
Recently I realized that if you only support one format (as in the above example), you don't really need that block. Is it best practice to leave it in, or remove it?
I'm going to differ with existing answers--I like to have a responds_to block for all of my actions. I find that, while slightly more verbose, it more clearly self-documents the action. It also makes it easy to support additional formats in the future. Edit: another advantage is it acts as a gatekeeper. Any format not declared in the block is automatically served a "406 Not Acceptable"
I'm not really sure if this is best practice or not, but usually what I like to do is to leave the routes open to respond_to (i.e. by appending .:format to the end), but only use it in the controllers when it's necessary.
Example:
routes.rb
map.connect :controller/:action/:id.:format
model_controller.rb
# Return a collection of model objects
def action_with_multiple_responses
#models = Model.all
respond_to do |format|
format.html #=> action_with_multiple_responses.html
format.xml { render :xml => #models }
end
end
# Return the first model object
def action_with_one_response
#model = Model.first
end
That way, you aren't cluttering up your action_with_one_response method with an unnecessary block, but you also have set yourself up quite nicely if you want to someday return your object in xml, json, etc.
I would say not to use respond_to unless you have multiple response types.
It is simply extra code to understand and for your app to process and handle:
render :json => #record
Is much more concise than:
responds_to do |format|
format.js { render :json => #record }
end