Suppose I have some #stuff, and I'm rendering a table with a few attributes about each thing. I want every other row to be colored differently, so I'm using the typical cycle trick:
%table
- #stuff.each do |thing|
%tr{ class: cycle('even', 'odd') }
%td thing.bleep
%td thing.bloop
But I also want to render certain rows differently based on some conditions related to each particular thing. So I want certain rows to also have the foo class if thing.foo? is true. If I weren't doing the cycle thing, I'd do:
%tr{ class: 'foo' if thing.foo? }
Is there a convenient way to do both of these things? Or do I really have to hand-write the ugly logic to conditionally append these things together with a space inbetween? It just feels like that sort of tedious logic is error-prone, and that there's an an abstraction I should be using to compose these two CSS class alterations.
You can use an array for class and id attributes:
The :class and :id attributes can also be specified as a Ruby array whose elements will be joined together. A :class array is joined with " " and an :id array is joined with "_".
So in this case you could do:
%tr{:class => [cycle('even', 'odd'), ('foo' if thing.foo?)]}
Any nils are removed from the array so you don’t end up with extra whitespace
Related
I have some data values that come in from a form, which are "stringified" versions of different data types - strings, arrays, hashes etc: they're all the result of calling "inspect" on the original data.
When I get them back, i want to save them into the object again in their original pre-inspect form - ie as a hash, an array, a symbol etc.
Obviously, i could just call "eval" on each value, but this data is from a form and so is susceptible to "Bobby Tables" sql injection, or even just a Ruby call like "User.delete_all" (which is harder to police - at least Rails has some methods for sanitizing sql). It's in an admin section of the site, and only a couple of trustworthy people will ever use it, but I'd still like to find a safer way.
To make things harder, this is in Ruby 1.8.6 and Rails 2.2.2 (Just don't - I know!).
Is there a way to reverse the "inspect" method without calling eval, or is this logically the same thing? Here's some example values that I want to convert.
"\"Instrument Home\"" => "Instrument Home"
":foo" => :foo
"[\"1311089\", \"1311128\", \"1310802\"]" => ["1311089", "1311128", "1310802"]
"{:foo=>\"bar\"}" => {:foo => "bar"}
EDIT: here's what my form looks like:
and how the form is set up:
<% #digilearning_module.metadata_keys_and_values.each do |num, hash| %>
<tr>
<td><%= text_field_tag "digilearning_module[metadata_keys_and_values][#{num}][key]", hash[:key].inspect %></td>
<td>=></td>
<td><%= text_area_tag "digilearning_module[metadata_keys_and_values][#{num}][value]", hash[:value].inspect %></td>
</tr>
<% end %>
and how the data comes back in (i've omitted most of the values from the big array, for clarity).
params[:digilearning_module][:metadata_keys_and_values] = {
"1"=>{"value"=>"[\"1311089\", \"1311128\", \"1310802\"]", "key"=>":tablet_compatible_child_mod_numbers"},
"2"=>{"value"=>"\"Instrument Home\"", "key"=>":special_category"}
}
When I process this, i discard the numbers which are just there to seperate the key and value out into a hash. Then I read the keys and data values back in as the keys and values of the hash which is actually saved into the db: so for the above I would want to end up with
{:tablet_compatible_child_mod_numbers => ["1311089", "1311128", "1310802"], :special_category => "Instrument Home"}
If you don't need symbol types, then a popular way to do that that I see on Stack Overlow is to use JSON instead of Ruby objects. You can map between Ruby objects and JSON, and safely encode and decode JSON.
In html.erb I have:
<%= ContactDescribe.where(["contact_describe_id = ?", "12"]).limit(1).pluck(:borrower_or_lender_text) %>
The field is retrieved successfully. But returns an array element. I need to learn how to convert that element to a string.
In addition to Deepak's answer, you can also convert the Array into a "Sentence" String
<%= ContactDescribe.where(contact_describe_id: 12).limit(1).pluck(:borrower_or_lender_text).to_sentence %>
Recommendation:
As pointed out by TheChamp, it is best practice to already "prepare" the values needed in the views as instance variables from the controller. See my recommended refactor
# controller
def YOUR_ACTION_NAME
#contact_describe = ContactDescribe.where(contact_describe_id: 12).first
end
# view
<%= #contact_describe.borrower_or_lender_text %>
Note: you won't even need pluck anymore unless you have other reasons why you want limit(1)
The issue here is that where returns a collection - something similar to an array, just in ActiveRecord - no matter what limit you set on it. To retrieve the information you would use .first or [0] since you always only return one object.
But, since you are looking for a specific ContactDescribe object. Do this instead:
ContactDescribe.find_by(contact_describe_id: 12).borrower_or_lender
Additionally there two things you should improve in your code.
1: Logic should go into the controller or the model. A view is solely here to show objects.
2: What is up with the contact_describe_id field? Why not call it id. It seems redundant. Isn't user.id more convenient than user.user_id?
You can make use of join
<%= ContactDescribe.where(contact_describe_id: 12).limit(1).pluck(:borrower_or_lender_text).join(',') %>
I'm printing a list, ordered as such:
<% #users.order(:number).each do |u| %>
<%= u.name %>
<% end %>
But some users will have the same value of :number, and in that case the records appear to be ordered based on time_created. How can I set a fallback or secondary ordering system to be used when records have the same value of the attribute used to order them?
You can just keep adding on to the order method.
#users.order(:number, :name, :last_logged_in, :id)
If the numbers are the same, it will break the tie with name, then last_logged_in, then id. This is all done in the database for you.
If you already had the data in memory, then you could use Ruby's sort_by method.
You're probably going to want to use sort_by instead of order.
#users.sort_by {|u| [u.number, u.second_option] }
that will sort first by number and then if number matches it will pull whatever the second option is and compare by that. Technically you can throw as many things to sort by as you want into that array.
Edit: Actually, there's a similar question already answered here
For database columns that are integers that "represent" strings, what is the best way to show the string value in the view?
For example, if I collect "payment_method" values as integers in my form as follows:
<%= f.select :payment_method, { "Visa" => "1", "Mastercard" => "2", "Amex" => "3"} %>
How can I show the saved integer as a string in my view files? What can I add to my model, so that
<%= #relevantvariable.payment_method %>
or something similar returns string values like "Visa", "Mastercard" or "Amex" instead of their respective integer values?
Thanks much for handling such a basic question!
Either don't use an integer value, and store the payment method directly as a string in the db, or create a PaymentMethod model.
With the association set up, you'd be able to refer to the name of the payment method as so:
<%= #relevantvariable.payment_method.name %>
Don't try to manually handle lists of names / ids - that will quickly get unmanageable.
Edit: after reading your comment, if you went with the first option and stored a string in the table, definitely don't allow the user to type the value directly, maintain a list on the model (or similar), that you seed your dropdown from, that way you're still constraining the possible values. You could even add a custom validator if you want to be certain what you putting in the database.
I'd stick with cheeseweasel solution but you can do one thing to show that on your view...
<% if #relevantvariable.payment_method == 1 %>
<%= "Visa" %>
<% else %>
<%= "Mastercard" %>
You probably would want to use a switch/case instead but you got the idea
As I said I think you should stick with cheeseweasel solution since there are many problems with this approach... it's your call
So you have your payment methods in a separate table payment_methods and the owner ( say user) contains a has_one relationship with it
class User < AR::Base
has_one :payment_method
end
Then show the payment method name just like
<%=
#user.payment_method.name #or whatever you have.
%>
However, while you are loading the #user data, you can perform a eager loading by :include. Like
User.find(user_id, :include => :payment_method)
This will load the PaymentMethod data earlier even for multiple users with single query.
I'm working on a Rails form that will allow the user to edit the attributes of many objects of a class with a single submission. My initial instinct was to create an outer form_for block and then iterate through the items within it using fields_for.
However, there is no object that bears a one-many relation to the objects the form will modify, and so it seems to me that there is no object that would be correct to pass into form_for.
In any case, what I'd like to see is an example of a form that modifies multiple objects simultaneously without appealing to a "parent" object. Perhaps this will involve form_tag?
(Note: I'm working in haml, so answers in haml would be awesome though unnecessary.)
Well, having a parent object will make it easier.
For bulk updates of many objects, the key is to use the right input name so that rails will parse the params as a array, i.e.
#posts.each do |post|
fields_for "posts[#{post.id}]", post do |p|
p.text_field :name
p.hidden_field :id
end
end
Have a look at the generated html source to see what name attribute the text input gets. If this is done right, params[:posts] will now be a hash in the controller which you can then update.
http://railscasts.com/episodes/165-edit-multiple should be relevant too
There are some extra wrinkles to my actual situation, but here's some pseudocode that I hope will illustrate the approach I've taken:
= form_tag url_for(:controller => "the_controller",
:action => "update") do
#objects_to_be_updated.each do |object|
= check_box_tag "[desired_path][through_the][parameters_hash]", true, object.boolean_attibute
= text_field_tag "[another_path][through_the][parameters_hash]", object.text_attribute
end
end
And so on.
Using the _tag variants of the form helpers, which don't require association with an Active Record model, is a bit of a pain but also seems to give you more control over structure of the resulting parameters.