render :json => #bs.to_a.to_json, :except => ["completo"]
I want to render everything to json except the field "completo". That should be working but given that I need to do ".to_a" and ".to_json", that stopped working. Is there a way to revert that?
Thanks
Assuming that #bs is a MongoDB Cursor, do the following:
#bs = #bs.to_a.map { |obj| obj.delete("completo"); obj }
render :json => #bs.to_json
In summary:
Make it an array.
Remove the completo key from every item in the array, making sure we return the item itself at the end of the map
Render as before.
Related
I have various values in a table that need to be updated after a DOM element is updated using best_in_place. How can you trigger a javascript action like "create.js.erb" called "update.js.erb" after a best in place update?
For example, I have a table of item prices, and I need the table's "total" field to update after a user updates an individual item quantity.
I've seen many people asking this question, but haven't found a satisfactory solution. I found the best approach is to create a javascript function that watches for a particular DOM element to update after an ajax:success, and then use jQuery to update a specified DOM element.
In the controller of the item being updated, I package a JSON response that includes all of the information my javascript will need in order to update DOM elements.
You'll see I also render the new table row's partial as an HTML string, and pass this string along with the JSON response.
___Item Controller_____
respond_to do |format|
if #item.update_attributes(params[:item])
#json response variables to refresh table row after update
id = [#item]
new_row = render_to_string('items/_item.html', :layout => false, :locals => { :item => #item })
#----------json-variables-----------#
format.json { render :json => { new_row: new_row, id: id, :status => 200 }}
format.js
else
format.json { render :json => #item.errors.full_messages, :status => :unprocessable_entity }
format.js
end
end
Then in a .js file that gets loaded with the page (like application.js), I include a function that watches for a table row with class "row_class" to be updated.
$(document).ready(function(row_class, row_id_prefix){
$("." + row_class).on("ajax:success",function(event, data, status, xhr){
var parsed_data = jQuery.parseJSON(data); //parses string returned by controller into json object
var id = parsed_data["id"];
var new_row = parsed_data["new_row"]; //this is an html string that replaces the existing table row
$('tr#' + row_id_prefix + id).replaceWith(new_row);
$('tr#' + row_id_prefix + id).effect('highlight');
$('.best_in_place').best_in_place(); //activates in-place-editing for newly added content.
}));
You can modify the jQuery to update any DOM element you need. Also, if you need the results of ruby methods called on objects (such as a humanized total, tax total, etc) you can define these as variables in the JSON object in the controller.
Ultimately it'd be best if best_in_place would trigger (in this case) the items/update.js.erb script, but it doesn't look like that's on the roadmap.
I'm trying to wrap my around a task and haven't found anyone who has address this issue.
I'm created an awards nomination system.
Anon user submits nomination
Admin user generates a merged letter from template and edits letter
Letter text is saved to database to later generate PDF
I read that you can render an ERB text to a variable. This is working great but then I'm stuck with the text because of the Double Render Error.
def generate_letter
#submission = Submission.find(params[:id])
#submission.letter_text = render (:text, :layout => false, :template => 'submissions/generate_letter') and return
#submission.save
redirect_to #submission
end
Is there a better way to generate this text for the database or a workaround to redirecting? I was trying to avoid keeping my merge template in the code and ERB seems a nice way to handle it.
You need to use render_to_string. This line
#submission.letter_text = render (:text, :layout => false, :template => 'submissions/generate_letter') and return
should be
#submission.letter_text = render_to_string( template: 'submissions/generate_letter.text.erb', layout: false )
I have a prescription model in my Rails 3 application. I am trying to work out the best method of allowing records to be duplicated, but allowing the user to "review" the duplicate before it's saved.
I have read a number of questions/answers on SO (such as this one) which explain how to duplicate/clone the record and then save it - but none which explain how to show the form before save.
Reading the Rails API is appears the clone method is available.
Reading other questions and answers shows that is can be done but there is no example code apart from:
new_record = old_record.dup
The controller code I am currently working with is as follows (the model doesn't have any relationships):
# POST /prescriptions
# POST /prescriptions.json
def create
#prescription = Prescription.new(params[:prescription])
#prescription.localip = request.env['REMOTE_ADDR']
#prescription.employee = #prescription.employee.upcase
respond_to do |format|
if #prescription.save
format.html { redirect_to #prescription, notice: 'Prescription was successfully created.' }
format.json { render json: #prescription, status: :created, location: #prescription }
else
format.html { render action: "new" }
format.json { render json: #prescription.errors, status: :unprocessable_entity }
end
end
end
I am going to be linking to this clone action from the view with:
<%= link_to "Create another like this?", clone_prescription_url(#prescription), :method => :put %>
Is it as simple as adding an action to my controller like this?
def clone
#prescription = Prescription.find(params[:id])
#prescription.dup
#prescription.save
end
Apologies if the above code is completely wrong, I'm trying to get my head around it! I've seen someone do exactly what I'm trying to achieve with the cloning - but not with the editing before save.
The user that's duplicating won't have permission to edit a record once saved. It's purely for the intial data entry.
To do this, you're going to have to create a new instance of your Prescription class. "dup" works, but you're assuming it overwrites the existing record. Only methods that end with a bang(!) tend to do that.
Your code should be:
def clone
#prescription = Prescription.find(params[:id])
#new_prescription = #prescription.dup
#new_prescription.save
end
or
def clone
#prescription = Prescription.find(params[:id]).dup
#prescription.save
end
This isn't testing for times when the :id isn't found.
If you want the clone action to allow the user to review the duplicate before it is saved (AKA created), then it is almost like the "new" action, except with filled in fields already.
So your clone method could be a modification of your new method:
def new
#prescription = Prescription.new()
end
def clone
#prescription = Prescription.find(params[:id]) # find original object
#prescription = Prescription.new(#prescription.attributes) # initialize duplicate (not saved)
render :new # render same view as "new", but with #prescription attributes already filled in
end
In the view, they can then create the object.
I was looking for logic to clone an existing record. I had to modify the logic posted by ronalchn slightly because when it tried to execute the second statement of clone I got an mass assignment error because it tried to copy id, created_at, updated_at which are not included in my attr_accessible list. This is how I modified the logic to get it to work in my application using my model:
#old_event = Event.find(params[:id]) # find original object
#event = Event.new
#event.field_1 = #old_event.field_1 (statement for each field in attar_accessible)
render :new # render same view as "new", but with #prescription attributes already filled in
I am trying to render a view + layout based on a condition. The following code seems to work but loses access to #objects set upfront. I call this code in a ProfilesController method Show.
#profiles = Profile.where(:user_id => current_user.id).first
if #profile.nil? == true
render :view => "show",
:layout => "application"
else
render :template => "profiles/my_profile",
:layout => "profiles"
end
Gives output:
undefined method `profiles' for nil:NilClass
How could one render based on a condition and still preserve the previous set #objects (in this case the access to #profiles)
Setting layout in view is not the way to go, its not the rails way and was not advised so even if possible. I changed setting layout in controller action and render the right code in my show partial wich complete solved the issue.
I don't even know how to write a proper title for this. I kind of cobbled together some routing code based on a bunch of different articles, and now I'm wondering if I've painted myself into a corner.
I've got a NewsArticle model, and I want the links to look like this:
/news # List of all articles
/news/2011 # List of articles published this year
/news/2011/06 # List of articles published this month
/news/2011/06/28 # List of articles published on this day
/news/2011/06/28/my-post-title # Actual article
Ok, going against the Rails way already, but so be it.
I've got routes setup like this:
controller :news_articles, :via => [:get] do
match '/news(/:year/(/:month(/:day)))' => :index, :constraints => { :year => /\d{4}/, :month => /\d{2}/, :day => /\d{2}/ }
match '/news/:year/:month/:day/:id' => :show
end
Note there is no :as declaration. That's because when I do add something like :as => "news_archive" then I end up with news_archive_path which returns something stupid like "/news?year=2010&month=4". So, I excluded that bit and wrote my own path methods in my application helper file:
def news_archive_path(year = nil, month = nil, day = nil)
return "/news" if year.nil?
t = Time.zone.local(year.to_i, month.nil? ? nil : month.to_i, day.nil? ? nil : day.to_i)
if month.nil?
"/news/#{t.year}"
elsif day.nil?
"/news/#{t.year}/#{"%02d" % t.month}"
else
"/news/#{t.year}/#{"%02d" % t.month}/#{"%02d" % t.day}"
end
end
def news_article_path(article)
t = article.published_at.in_time_zone
"#{news_archive_path(t.year, t.month, t.day)}/#{article.friendly_id}"
end
Great, this all works in practice. But now I've run into a problem where I'm testing my controllers and I want to make sure that the right links appear on the rendered templates. (Oh yeah, I'm not keeping separate view tests but instead using render_views in my controller tests.) But the tests are failing with the error undefined methodnews_article_path' for #`.
So, have I just approached this all wrong and painted myself into a corner? Or can I get out of this by somehow including the helper methods in the controller test? Or do I just suck it up for the sake of getting the test to pass and hardcode the links as I expect them to be?
To make news_article_path available you need to do:
class MyClass
include Rails.application.routes.url_helpers
end
Now MyClass#news_article_path will be defined. You can try to include the url_helpers into your test case. I've seen that break recently with "stack level too deep" errors (it creates an infinite recursion of "super" calls in initialize). If it doesn't work, create your own view helpers class and do "MyViewHelper.new.news_article_path" etc.
It's probably not a good idea to be generating the paths by concatenating string in your path helpers. Instead, simply use the url_for() method. Eg:
def news_article_path(article, options = {})
url_for(options.merge(:controller => :articles,
:action => :show,
:id => article.id,
:year => article.year,
:month => article.month,
:day => article.day))
end
(This assumes day, month, year are added as quick methods on your model, eg:
def year
self.published_at.in_time_zone.year
end
For your tests, either include the helper methods via something along these lines: My helper methods in controller - or use the url_for method again.
...That said - as you suggested, testing views within controller tests isn't ideal. :)