Best way to hook into a Rails 3 page lifecycle - ruby-on-rails-3

I have the following scenario: I am adapting a edit in place library to be able to display formatted values after the value is edited on an input field.
For that, I want to store the id for some fields being rendered on a given controller action to be served via AJAX whenever needed.
I want to be able to do the following in a view:
<%= edit_in_place #object, :attribute do |value|
"Formatted display value is #{value}"
end
%>
So, I generate a UUID for the control being rendered:
<input ... data-uuid="27b52850-d68f-012e-5dc8-28373723e63c" ...>
The ultimate goal is to keep a FormattingRules hash that would take the block being passed and assign it to the UUID so I could later on, after the user has edited the value, call an AJAX method to format the value:
http://server/format/27b52850-d68f-012e-5dc8-28373723e63c?value=My+Value
The following code is triggered:
def show
block = BestInPlace::FormattingRules[params[:id]]
render :text => block.call(params[:value])
end
And the cod returns:
Formatted display value is My Value
Which is great. With this, I can hide the edit in place input and display the formatted value.
Everything works already, however the FormattingRules hash is growing indefinitely and never being emptied.
My question is: is there a way I can hook into the Rails lifecycle and make this hash more granular (per page or session, maybe?) and make sure it's gone after the page is no longer being used?

Related

rails form fields_for serialize hash

I need to create a check list for a comment form on which users to notify about a comment.
I have a comment model and i have a column called comment_meta which i want to store as a serialize hash.
My comment form has a fields_for comment_meta example;
<%= f.fields_for :comment_meta do |comment_form| %>
<%= comment_form.check_box(contact.id) %>
<% end %>
The params being passed are "comment_meta"=>{"155"=>"0", "156"=>"1", "157"=>"0"}},
but my db is saving an empty hash.
The field type for comment_meta is a text.
Is there a way to save this?
Your comment_meta hash seems to be a 'key-value pair' of ids and Boolean values, respectively. If this is true, then you may be better off going with a users_comment_metas 'through' table.
You would benefit from restructuring to this methodology because:
db indexing is fast - rails hash serialization/parsing to and from the db is slow (unless your using postgres's h-store, but thats another story)
Dynamic reporting about all data in your db will be much more intuitive/query-able as a developer attempting to display readable reports (which is very important at my job at least).
Your goal of using Rails' built in form helpers will be much easier.
If you truly want to go the hash route you've suggested, there could be a variety of things preventing it from saving. Here's how I would troubleshoot:
First make sure you have the line 'serialize comment_meta, Hash' in your Comment model.
Remove ALL validations you may have in the Comment model for testing purposes.
Now, type 'debugger' (no quotes) in your controller create and/or update
action in your comments_controller (or the controller you are
requesting from the form)
when you hit your debugger statement in your terminal, type the words
'eval comment_params' to check the comment params you've submitted (I am assuming you are using the strong parameters gem, seeing as you typed 'ruby-on-rails-4' in your tags for this post).
The hash should have this structure as you've suggested above (if it looks different,
something is wrong in your form, or you have strong paramters set up incorrectly, more info here):
"comment_meta"=>{"155"=>"0", "156"=>"1", "157"=>"0"}
type 'n' until you've passed the line initializing a new object with the params
#comment = Comment.new(comment_params)
Now type 'eval #comment' to see your new object, and then type 'eval #comment.comment_meta' to see if the hash is being stored. If it's returning nil, then type 'eval #comment.valid?'. If it returns true, then type #comment.errors to see what is wrong, and fix accordingly.
If all else fails, make sure you can do a manual assignment of any given hash to comment_meta of a new Comment object in your rails console:
#comment = Comment.new()
#comment.comment_meta = {"test"=>"1"}
#comment.save!
I hope this gets you on the right track.

Rails Displaying Flash Messages from Model

I'm trying to display some error messages from my model in Rails. Currently, when an exception is caught in my test model and raised, the messages are returned as expected in the errors scope of my object. The messages are returned as follows:
{:key1=>["Your key needs to be different."],
:key2=>["Another exception"]}
This is perfect, as the correct exceptions are being thrown for the correct errors. What I'm trying to do now is bind these messages to the flash scope so that I can display them on my view. In my controller, when I have an exception, I then bind it to the flash[:error] scope as follows:
flash[:error] = #test.errors.messages
render :new
Upon doing this, my errors are bound to the flash scope, but when I output them on my view, they are displayed as follows:
[:error, {:key1=>["Your key needs to be different."], :key2=>["Another exception"]}]
All I'd like to do is display each one of these messages so that they look to be like:
Your key needs to be different.
Another exception.
Notice that in the above example, all that will be rendered is the message text.
Will you please point me in the right direction as to what I need to change either in my controller or in my view to achieve the desired output above?
Thank you in advance.
The one place I immediately see for improvement is in the hash you're passing.
If there's not a specific reason you're wrapping your strings in an array, you can eliminate the array:
{
:key1 => "Somebody poisoned the watering hole!",
:key2 => "There's a snake in my boot."
}
That will both simplify your code and eliminate the two-dimensional array you didn't seem to be intending to create.
If there is a reason you're wrapping your strings in an array (multiple messages per key?), you'll need to revisit your iterator; the way it's written now, it's only going to display the first string for any given key.
I noticed that when I looped over #test.errors.messages in the following construct, I ended up with a 2-Dimensional array:
[:key1, ["Your key needs to be different."]]
[:key2, ["Another exception"]]
Since this is a multidimensional array, I was able to then simply loop over the array in my erb and get the value to display as requested in my question:
<% for i in flash[:error] %>
<%= i[1].first %><br />
<% end %>
Returns on the screen:
Your key needs to be different.
Another exception.
I'm always open to suggestions, so if there's a better way to handle this, then please advise! Otherwise, this does work for me.

rails user input with <script>, stored, and displayed

I have an application that collect user input and store to DB and show back to user.
One user entered "alert(1)" into the name field and saved it into DB.
Whenever the name is displayed, the page will be broken.
I know how to fix that input only with validation for input, and h() for output.
However, I have so many input fields and so many outputs that accept users' text.
Is there any simple way to prevent this happening(i.e. overriding params method, etc)?
I also want to know how you expert guys are dealing with this problem?
As of Rails 3, my understanding was that embedded ruby code was html escaped by default. You don't need to use h() to make it that way. That is, if you use <%= "<script>a=1/0;</script>" %> in a view, the string is going to be made html safe, and so the script doesn't execute. You would have to specifically use raw() or something similar to avoid it - which you should naturally not do unless you're really confident about the contents.
For output, Rails 3 automatically html-encode all text unless I use raw() method.
For input, How about making a common validator and apply to all fields that are text or string? Is it desirable?
http://api.rubyonrails.org/classes/ActiveModel/Validator.html
class MyValidator < ActiveModel::Validator
def validate(record)
record.class.columns.each do |c|
if c.type==:text || c.type == :string
record.errors.add c.type, "script tag is not allowed" if c[/<script[^>]*>/]
end
end
end
end

Validate number of attributes with date_select

I'm doing something like the following: Validate number of nested attributes.
I'm checking for existence of at least one nested attribute.
This was working fine when I was using a text input for the date but I've changed it to use a date_select instead and now the same validation code shows an error saying that not enough have been chosen.
When it fails validation and reloads the form it also doesn't "build" an instance of the nested attribute either so it just shows my "[+]" link
Anybody got any ideas?
Failing everything I'll just have to put the text field back (probably using type=date).
Col
I decided to just put the text field back.

How can I store extra data with a validation error message in Rails 3?

I'm trying to store some additional data along with the standard error message in a custom validator in Rails 3.
For example, (ignoring built-in validators) suppose I wanted to check to see if a post is a duplicate before it's saved. I might write my custom validation method like this:
class Post < ActiveRecord::Base
# prevent duplicate posts
validate do |post|
duplicates = Post.find_all_by_body(body)
errors.add_to_base("Post is a duplicate!") if duplicates.length
# something like this is desired:
# errors.add_to_base("Post is a duplicate",
# :extra => { :duplicates => duplicates })
end
end
This will let the user know there are duplicates, but along with adding the error message I would also like to store the duplicates so they can be displayed to the user. How would I store the list of duplicate posts retrieved during validation such that it is associated with the record's errors for the body field, and available to my view?
A simpler example might be length validation: If a field exceeds its maximum length, how can I store the maximum length along with an error message without simply interpolating it into the message as Rails currently does?
I have not had to do this before, but my first thought is to create a new method on the object called duplicates.
attr_accessor :duplicates
Then in your custom validate method, you can set the duplicates on the object making them available to the view when you render the errors. Notice your current code doesn't change much:
validate do |post|
duplicates = Post.find_all_by_body(body)
errors.add_to_base("Post is a duplicate!") if duplicates.size > 0
end
You would then have to intercept that error in the view manually so that you can print out all the duplicates if the "Post is a duplicate!" error is encountered.
You can pass options to the error but they are only used as substitution in i18n templates. To make a long story short, no you can't store meta-data about your error in the errors hash. If you need such a functionality you'll need to look into the ActiveModel::Errors module in Rails core.
Update:
Another solution could be that instead of pushing a string into error hash, you stuff an instance of your own class, a class which quacks like a string but would be decorated with extra methods and state and such like.