Rails 4.0 Strong Parameters nested attributes with a key that points to a hash - ruby-on-rails

I was playing around with Rails 4.x beta and trying to get nested attributes working with carrierwave. Not sure if what I'm doing is the right direction. After searching around, and then eventually looking at the rails source and strong parameters I found the below notes.
# Note that if you use +permit+ in a key that points to a hash,
# it won't allow all the hash. You also need to specify which
# attributes inside the hash should be whitelisted.
https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/strong_parameters.rb
So its saying you have to specify every single every single attribute within the has, I tried the following:
Param's example:
{"utf8"=>"✓",
"authenticity_token"=>"Tm54+v9DYdBtWJ7qPERWzdEBkWnDQfuAQrfT9UE8VD=",
"screenshot"=>{
"title"=>"afs",
"assets_attributes"=>{
"0"=>{
"filename"=>#<ActionDispatch::Http::UploadedFile:0x00000004edbe40
#tempfile=#<File:/tmp/RackMultipart20130123-18328-navggd>,
#original_filename="EK000005.JPG",
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"screenshot[assets_attributes][0][filename]\"; filename=\"EK000005.JPG\"\r\nContent-Type: image/jpeg\r\n">
}
}
},
"commit"=>"Create Screenshot"}
Controller
def screenshot_params
params.require(:screenshot).permit(:title,
:assets_attributes => [:filename => [:#tempfile,:#original_filename,:#content_type,:#headers]
The above isn't "working" (its not triggering carrierwave) however I am no longer getting errors (Unpermitted parameters: filename) when using the standard nested examples I found ex:
def screenshot_params
params.require(:screenshot).permit(:title, assets_attributes: :filename)
If anyone could help it would be great. I was not able to find a example with nested with a key that points to a hash.

My other answer was mostly wrong - new answer.
in your params hash, :filename is not associated with another hash, it is associated with an ActiveDispatch::Http::UploadedFile object. Your last code line:
def screenshot_params
params.require(:screenshot).permit(:title, assets_attributes: :filename)
is actually correct, however, the filename attribute is not being allowed since it is not one of the permitted scalar types. If you open up a console, and initialize a params object in this shape:
params = ActionController::Parameters.new screenshot: { title: "afa", assets_attributes: {"0" => {filename: 'a string'}}}
and then run it against your last line:
p = params.require(:screenshot).permit(:title, assets_attributes: :filename)
# => {"title" => "afa", "assets_attributes"=>{"0"=>{"filename"=>"abc"}}}
However, if you do the same against a params hash with the uploaded file, you get
upload = ActionDispatch::Http::UplaodedFile.new tempfile: StringIO.new("abc"), filename: "abc"
params = ActionController::Parameters.new screenshot: { title: "afa", assets_attributes: {"0" => {filename: upload}}}
p = params.require(:screenshot).permit(:title, assets_attributes: :filename)
# => {"title" => "afa", "assets_attributes"=>{"0"=>{}}}
So, it is probably worth a bug or pull request to Rails, and in the meantime, you will have to directly access the filename parameter using the raw params object:
params[:screenshot][:assets_attributes]["0"][:filename]

So, you're dealing with has_many forms and strong parameters.
This is the part of the params hash that matters:
"assets_attributes"=>{
"0"=>{
"filename"=>#<ActionDispatch::Http::UploadedFile:0x00000004edbe40
#tempfile=#<File:/tmp/RackMultipart20130123-18328-navggd>,
#original_filename="EK000005.JPG",
#content_type="image/jpeg",
#headers="Content-Disposition: form-data; name=\"screenshot[assets_attributes][0][filename]\"; filename=\"EK000005.JPG\"\r\nContent-Type: image/jpeg\r\n">
}
}
when you define strong parameters like this...
permit(:assets_attributes => [:filename])
Things break, because where rails expects a filename it's getting this "0"
What does that number mean? It's the id for the asset you are submitting via your form. Now initially you might think you have to do something like
permit(:assets_attributes => [:id => [:filename]])
This looks like it follows other strong parameters syntax conventions. However, for better or for worse, they have made things a little easier, and all you have to write is:
permit(:assets_attributes => [:asset_id, :filename])
Edit -
As jpwynn pointed out in the comments, in Rails 4.2.4+ the correct syntax is
permit(:assets_attributes => [:id, :filename])
and that should work.
When you hit walls with strong params, the best thing to do is throw a debugger in your controller and test things out. params.require(:something).permit(:other_things) is just a method chain so you can try out different things on the full params hash until you find what works.

try
def screenshot_params
params.require(:screenshot).permit(:title, :assets_attributes => [:filename, :id, :screenshot_id])
end
I had this issue about a month ago and some searching around dug up this solution. It was adding the :id or :screenshot_id that fixed the problem (or both, I can't remember). This works in my code though.

Actually there is a way to just white-list all nested parameters.
params.require(:screenshot).permit(:title).tap do |whitelisted|
whitelisted[:assets_attributes ] = params[:screenshot][:assets_attributes ]
end
This method has advantage over other solutions. It allows to permit deep-nested parameters.
While other solutions like:
params.require(:screenshot).permit(:title, :assets_attributes => [:filename, :id, :screenshot_id])
Don't.
Source:
https://github.com/rails/rails/issues/9454#issuecomment-14167664

I had same problem just got it fixed now all you have to do is
params.require(:vehicle).permit(:user_id, assets_attributes: [:id, :image]).
Use pry gem to see what kind of attributes your asset object has makes sure theres an id and add other missing attribute, that should then work perfectly.
Am using paperclip assets is my nested object inside the vehicle class and an attachment of images is added to the asset.
make sure you do the validation in the model
accepts_nested_attributes_for :assets, allow_destroy: true
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
In your view loop through the asset to get each image
<%= #vehicle.assets.size %>
<% for asset in #vehicle.assets %>
<%=link_to image_tag (asset.image.url(:thumb)) %>
<% end %>
If am correct your problem is that asset_attributes is an array with each image having an index column and an image
Your form_for should have something similar to this and if you want you can also include preview so the upload can view their images use the bottom code for that
<div class="field">
<h3>Vehicle Image Upload</h3>
<%= f.fields_for :assets do |asset_fields| %>
<% if asset_fields.object.new_record? %>
<p>
<%= asset_fields.file_field :image %>
</p>
<% end %>
<% end %>
</div>
<div class="field">
<h4>Vehicle Image</h4>
<%= f.fields_for :assets do |asset_fields| %>
<% unless asset_fields.object.new_record? %>
<%= link_to image_tag(asset_fields.object.image.url(:thumb)),
asset_fields.object.image.url(:original)%>
<%= asset_fields.check_box :_destroy %>
<% end %>
<% end %>
</div>

Sanitize before save in controller Sanitize accepts_nested_attributes_for attributes with index.
before_action :sanitize_fields_params, :only => [:create, :update]
def sanitize_fields_params
product_free_shippings_attributes = params[:product][:product_free_shippings_attributes]
product_free_shippings_attributes.each do |index, key_value|
params[:product][:product_free_shippings_attributes]["#{index}"][:weight] = clear_decimal(key_value[:weight])
params[:product][:product_free_shippings_attributes]["#{index}"][:height] = clear_decimal(key_value[:height])
params[:product][:product_free_shippings_attributes]["#{index}"][:width] = clear_decimal(key_value[:width])
params[:product][:product_free_shippings_attributes]["#{index}"][:depth] = clear_decimal(key_value[:depth])
end
end
def clear_decimal(field)
return (field.to_s.gsub(/[^\d]/, '').to_d / 100.to_d) unless field.blank?
end

Related

How to accept multiple values into the same key parameter, as an array?

After reading through quite a few threads here on SO as well as various book/web resources, I'm still at a loss for how to properly set up a parameter key so that it will take in multiple values on a single form.
The readme for strong_parameters seems to indicate this is possible by declaring the item in the params hash as an empty array, like this params.permit(:id => []).
However, when I set up my params hash this way, it still manages to return only the last value for the key that was set in the form, rather than an array of all the values submitted by the user.
I notice too that the param key is showing to be a string, rather than an array. Running params[:document][:key].class returns String rather than Array. Also, when I experimented with including a "name=key[]" option in the form's text_field it complains of an undefined method 'merge' for "key[]":String.
Any idea where I'm going wrong? I admit some of the controller code may be a bit rough, but at this point, I'm just trying to get the params hash to return an array of k/v pairs. Thank you in advance for your help!
documents_controller.rb
before_action :get_key_array, only: [:create, :update]
before_action :get_value_array, only: [:create, :update]
before_action :combine_key_array_and_value_array_to_create_jsonb_object, only: [:create, :update]
before_action :save_jsonb_object_to_user_defined_attributes_field, only: [:create, :update]
...
private
params.require(:document).permit(:key => [], :value => [])
# FIXME: Update to use push << ?
def get_key_array
unless params[:document].nil?
#key_array = [params[:document][:key]]
# #key_array << params[:document][:key]
end
end
def get_value_array
unless params[:document].nil?
#value_array = [params[:document][:value]]
# #value_array << params[:document][:value]
end
end
def combine_key_array_and_value_array_to_create_jsonb_object
unless #document.nil? || #key_array.nil? || #value_array.nil?
#jsonb_object = jsonb_object(#key_array, #value_array)
end
end
def save_jsonb_object_to_user_defined_attributes_field
params[:document][:user_defined_attributes] = #jsonb_object
end
_form.html.erb
<%= form_for #document do |document_form| %>
<%= document_form.label :key %>
<%= document_form.text_field :key %>
<%= document_form.label :value %>
<%= document_form.text_field :value %>
<%= document_form.label :key %>
<%= document_form.text_field :key %>
<%= document_form.label :value %>
<%= document_form.text_field :value %>
<% end %>
After inputting test data like Key1, Value1, Key2, Value2, the params hash indicates that only the second set of input values are being added to the params hash
Parameters: { ..., "document"=>{"key"=>"key2", "value"=>"value2"}, "commit"=>"Create Document"}
UPDATE: If it matters, the :key and :value arrays are, themselves, not part of the document model. I used attr_accessor :key, :value to give the array values a "temporary" home to be used in the form and params hash, with the intention of transferring their values to the user_defined_attributes field, which is part of the document model. Just throwing this out there in case it's part of the issue.
Thank you to #TarynEast and #mu_is_too_short for their comments on this! You both are so kind with your time and StackOverflow is better for having you in its community! :) Thank you thank you!
Now, on to the solution...
Since I was setting up array variables (ie key and value) to temporarily store parameters that were not directly part of the Documents table, the solution was two-fold.
First I had to properly initialize these variables before using them in the controller.
At first, I experimented in document.rb with
def initialize
#key = Array.new
#value = Array.new
end
but this threw some argument errors. I'm not sure why exactly, but I suspect Rails 5 frowns on the use of initialize since ActiveRecord uses it further up the chain to automatically set up attr_accessor methods...
So then I modified the array creation to use callbacks, and that did the trick!
document.rb
after_initialize :create_key, :create_value
def create_key
#key = Array.new
end
def create_value
#value = Array.new
end
Then, with the variables properly set up as arrays, the form now let me name the fields with array references (previously, it rightly complained about key and value being strings).
_form.html.erb
<%= document_form.text_field :key, name: "key[]" %>
<%= document_form.text_field :value, name: "value[]" %>
Thoughts? Critiques? Improvements? I'm all ears and willing to learn :)

How to handle data from nested forms in Rails 4 with cocoon gem?

I am using Cocoon gem to do nested forms.
I have models like that:
# request.rb
has_many :filled_cartridges, inverse_of: :request, dependent: :destroy
accepts_nested_attributes_for :filled_cartridges, :reject_if => :all_blank, allow_destroy: true
#filled_cartridge.rb
belongs_to :request
Inside of my form_for #request i have nested form like that:
<div id="filled_cartridges">
<%= f.fields_for :filled_cartridges do |filled_cartridge| %>
<%= render 'filled_cartridge_fields', f: filled_cartridge %>
<% end %>
<div class="links">
<%= link_to_add_association 'add', f, :filled_cartridges %>
</div>
Where filled_cartridge_fields partial is like that:
<fieldset>
<%= f.text_field :cartridge_id %>
<%= f.hidden_field :_destroy %>
<%= link_to_remove_association "remove", f %>
</fieldset>
When i click on "add" it adds one more . When clicking on "remove" it removes that .
When i submit form the params for nested form look like that:
filled_cartridges_attributes: !ruby/hash:ActionController::Parameters
'0': !ruby/hash:ActionController::Parameters
cartridge_id: '12'
_destroy: 'false'
'1429260587813': !ruby/hash:ActionController::Parameters
cartridge_id: '2'
_destroy: 'false'
How do i access these params, and how to save them. How to traverse over these params and save them, or do Cocoon gem has some built in functionality? And finally how to check if these params are set? Since it is nested, it tricks me.
EDIT: My request_controllers#create:
def create
#request = Request.new( request_params )
# code for handling Request model
# here i want to handle nested model too (filled_cartridge)
#request.save
if #request.save
flash[:success] = "Заявка была добавлена"
redirect_to #request
else
render 'new'
end
end
EDIT2: my strong params:
def request_params
params.require(:request).permit(:name, :address, :phone, :mobile, :type, :description, :priority, :responsible, :price, :payed, :date, filled_cartridges_attributes: [:cartridge_id, :_destroy], :stype_ids => [], :social_media =>[])
end
In a recent project using cocoon I had to access the params of the attributes about to be saved. I figured a code in my create action in my controller. The trick is to understand how to retrieve the key of the hash of the attribute that is about to be saved. The key of the hash is that number '1429260587813' that is in your params
...
'1429260587813': !ruby/hash:ActionController::Parameters
cartridge_id: '2'
_destroy: 'false'
So you need to create a loop in your create action to retrieve this key using ruby hash method "keys". I do a loop because when using cocoon dynamic nested field I might create more than one nested attributes at once so it means more than one key to retrieve.
Here is a the code that worked for me, read my comments which explains the different steps of this code. I hope it will help you to adapt it to your needs.
#Here I just initialize an empty array for later use
info_arr = []
#First I check that the targeted params exist (I had this need for my app)
if not params[:recipe]["informations_attributes"].nil?
#z variable will tell me how many attributes are to be saved
z = params[:recipe]["informations_attributes"].keys.count
x = 0
#Initiate loop to go through each of the attribute to be saved
while x < z
#Get the key (remember the number from above) of the first hash (params) attribute
key = params[:recipe]["informations_attributes"].keys[x]
#use that key to get the content of the attribtue
value = params[:recipe]["informations_attributes"][key]
#push the content to an array (I had to do this for my project)
info_arr.push(value)
#Through the loop you can perform actions to each single attribute
#In my case, for each attributes I creates a new information association with recipe
#recipe.informations.new(title: info_arr[x]["title"]).save
x = x +1
end
end
This work to access cocoon nested attribute content and apply actions based on your need. This worked for me so you should be able to use this sample code and adapt it to your need.

Ruby on Rails - JS Input token, an issue when validation fails

I have a company model which can have many tags. It works fine, but in one occasion it does not work. The occasion is when company model validation fails. After :render => 'edit' it does not show tags in the view. I suspect the data-pre is not taking the data correctly. I would also like for tags to be preserved when solving validations.
I got this idea from here: http://railscasts.com/episodes/167-more-on-virtual-attributes
I use Input token control: http://loopj.com/jquery-tokeninput/
This is what I have in Company model regarding the tag_tokens:
before_save :save_tag_tokens
attr_writer :tag_tokens
attr_accessible :tag_tokens
def tag_tokens
#tag_tokens || tags.to_json(:only => [:id, :name])
end
def save_tag_tokens
if #tag_tokens
#tag_tokens.gsub!(/CREATE_(.+?)_END/) do
Tag.create!(:name => $1.strip.downcase).id
end
self.tag_ids = #tag_tokens.split(",")
end
end
Here is the code from the view:
<div class="input text no-border">
<% Tag.include_root_in_json = false %>
<%= company_form.label :tag_tokens, t('form.account.company.edit.company_tags_html')%>
<%= company_form.text_field :tag_tokens, :id => 'company_tag_tokens', "data-pre" => #company.tag_tokens%>
<p class="tip"><%= t('form.account.company.edit.tag_tip') %></p>
</div>
EDIT:
OK, so I see what is the problem with the above code.
When i load edit page data-pre contains this: data-pre="[{"id":1704,"name":"dump truck"}]". when I submit the form with validation error the data-pre contains: data-pre="1704".
if i change the code to this:
def tag_tokens
tags.to_json(:only => [:id, :name])
end
new tags that were not yet save to the company model are removed, because they are read from the DB everytime. How can I preserve the entered data between form transitions?
OK, I've written a solution, it might not be the nicest one, but it works to me! It parses the input token value to JSON format (when validation fails), which is used when loading the page. Under page load it just loads tags from DB.
def tag_tokens
if #tag_tokens
#if there is user info, parse it to json format. create an array
array = #tag_tokens.split(",")
tokens_json = []
#loop through each tag and check if it's new or existing
array.each do |tag|
if tag.to_s.match(/^CREATE_/)
#if new generate json part like this:
tag.gsub!(/CREATE_(.+?)_END/) do
tokens_json << "{\"id\":\"CREATE_#{$1.strip.downcase}_END\",\"name\":\"Add: #{$1.strip.downcase}\"}"
end
else
#if tag is already in db, generate json part like this:
tokens_json << "{\"id\":#{tag},\"name\":\"#{Tag.find_by_id(tag).name}\"}"
end
end
#encapsulate the value for token input with [] and add all tags from array
"[#{tokens_json.to_sentence(:last_word_connector => ',', :words_connector => ',', :two_words_connector => ',')}]"
else
#if there is no user input already load from DB
tags.to_json(:only => [:id, :name])
end
end

Paperclip functional test giving NoHandlerError [duplicate]

Using Paperclip 3.0.1 in rails 3.2.2 I got this error:
**Paperclip::AdapterRegistry::NoHandlerError**
(No handler found for "2009-11-29-133527.jpg"):
In my model I have:
class Product < ActiveRecord::Base
...
has_many :assets
accepts_nested_attributes_for :assets
end
class Asset < ActiveRecord::Base
belongs_to :product
has_attached_file :image,
:path => ":rails_root/public/system/:attachment/:id/:style/:filename",
:url => "/system/:attachment/:id/:style/:filename",
:styles => { :medium => "300x300>", :thumb => "100x100>" }
end
The exception is raised at:
def create
**#product = Product.new params[:product]**
...
end
with params:
{...,
"product"=>{"title"=>"wibble1",
**"assets_attributes"=>{"0"=>{"image"=>"2009-11-29-133527.jpg"}
},**
"description"=>"Who is wibble...",
"price"=>"23.45"
},
"commit"=>"Create Product",
...}
Anyone know what's going on?
This Error is raised because you aren't giving Paperclip a correct class. It's a just a String.
You should receive something like this in params
"asset"=>
{"image"=>
#<ActionDispatch::Http::UploadedFile:0x000000056679e8
#content_type="image/jpg",
#headers= "Content-Disposition: form-data; name=\"asset[image]\";
filename=\"2009-11-29-133527.jpg\"\r\nContent-Type: image/jpg\r\n",
#original_filename=""2009-11-29-133527.jpg"",
#tempfile=#<File:/tmp/RackMultipart20120619-1043-yvc9ox>>}
And you should have something like this in yout View (in HAML, very simplified):
= form_for #product, html: { multipart: true } do |f|
= f.fields_for :asset do |asset_form|
= asset_form.file_field :image
Remember to set your form to multipart: true.
I just ran into this problem myself. In my case it was caused by skipping the multipart form declaration in the markup.
I was using formtastic so I added this and got it working:
semantic_form_for #picture, :html => {:multipart => true} do |f|
Note there is a situation when working with an HTML5 canvas that is worth noting. Getting the canvas data as a DataURI string and sending that to server can cause this error. Canvas .toDataURL() will give something like "data:image/png;base64,iVBORw0KGg..." which you can send to server with other information, so its different than a standard multi-part form upload. On the server side if you just set this to the paperclip attachment field you will get this error. You need to conver it to a file or IO object. You could write a temp file like this:
data_uri = params[:canvasDataUri]
encoded_image = data_uri.split(",")[1]
decoded_image = Base64.decode64(encoded_image)
File.open("signature.png", "wb") { |f| f.write(decoded_image) }
or use Ruby's StringIO which acts like an in memory file interface
#docHolder.document = StringIO.new(decoded_image)
Hope this helps.
I had <input type="file" ... multiple="multiple"> on file input, so paperclip attachment data was in an array.
I solved this simply by removing multiple attribute on file input.
my problem was not accepting get method in routes so i changed it as patch method and it work fine.
<%= form_for #product, :url => "/products/#{#product.id}/upload",:method => :patch, :html => { :multipart => true } do |f| %>
I'm pretty sure your problem is with the form_for in the view,
try something like this:
<%= form_for #restaurante, :html => { :multipart => true } do |form| %>
Nome:<%= form.text_field :nome%>
Endereço:<%= form.text_field :endereco %>
Especialidade:<%= form.text_field :especialidade %>
Foto:<%= form.file_field :foto %>
<%= form.submit 'create'%>
<% end %>
In my case I was passing String, as in #MauricioPasquierJuan's answer, but I wasn't using a form so the rest of the answer doesn't apply.
I couldn't find any documentation of how to programatically update an attachment - what types can be assigned, and why assigning and saving the modified record does not save modified attachments. This question was the closest thing I found.
Inside a function that process files inside an uploaded zip file, after saving extracted files to temp files, this is my solution:
record.attachment = File.new( tempfile_path_as_string ) ## probably not the only option
record.attachment.save ## next line doesn't update attachment without this
record.save
Make sure you migrate the database after you install Paperclip ('rake db:migrate')... Also, you might need to add the new data fields generated by Paperclip to your 'attr_accessible' line in the model.
I had a similar problem when I was trying to get Paperclip workin on one of my projects.
I have met the same problem, I think it is because there are two tables sharing the same attached_file_name... In my case, I add a :photo column both to activities and tweets, then the case seems to be that the system can find one of them but not the other. Because the files are saved in /public/photo/:id/:id path, if you have two columns both named as photo, then the problem occurs I think.
for me the problem was like this:
I used such line in controller, as saw it in some answers:
#image = User.find(params[:id]).image.<b>path</b>(:small)
and I had the problem "no handler for the file"
so, I just removed "path" and it worked:
#image = User.find(params[:id]).image(:small)
When we upgraded from 4.2.x to 4.3.x, we had to change the main paperclip field attribute (picture, image, etc) from a relative URL to a full URL to eliminate this error.

How to display Rails select field values rather than stored integers in other views

I'm using a select field in a Rails app that is NOT tied to a related model, but stores integer values for a static series of options , i.e.,
<%= select (:this_model, :this_field, [['Option1',1],['Option2',2],['Option3',3],['Option4',4]] ) %>
In a show/ index view, if I want to display the option text (i.e. Option1, Option2, etc) rather than the integer value stored in the database, how do I achieve this?
Thanks for helping a noob learn the ropes!
EDIT
Based on Thorsten's suggestion below, I implemented the following. But it is returning nil, and I can't figure out why.
Invoice model:
##payment_status_data = { 1 => "Pending Invoice" , 2 => "Invoiced" , 3 => "Deposit Received", 4 => "Paid in Full"}
def text_for_payment_status
##payment_status_data[payment_status]
end
Invoice show view:
Payment Status: <%= #invoice.text_for_payment_status %>
In the console:
irb > i=Invoice.find(4)
=> [#<Invoice id: 4, payment_status: 1 >]
irb > i.text_for_payment_status
=> nil
I've tried defining the hash with and without quotes around the keys. What am I missing?
something like this would work:
<%= form_for #my_model_object do |form| %>
<%= form.label :column_name "Some Description" %>
<%= form.select :field_that_stores_id, options_for_select({"text1" => "key1", "text 2" => "key2"}) %>
<% end %>
Update
If you later want to display the text you can get it from a simple hash like this:
{"key1" => "text 1", "key2" => "text2"}[#my_object.field_that_stores_id]
But you better store this hash somewhere in a central place like the model.
class MyModel < ActiveRecord
##my_select_something_data = {"key1" => "text 1", "key2" => "text2"}
def text_for_something_selectable
##my_select_something_data[field_that_stores_id]
end
end
Then you can use it in your views like
#my_object.text_for_something_selectable
There are many possible variations of this. But this should work and you would have all information in a central place.
Update
Ok, I used something similar for our website. We need to store return_headers for rma. Those need to store a return reason as a code. Those codes are defined in an external MS SQL Server Database (with which the website exchanges lots of data, like orders, products, and much more). In the external db table are much more return reasons stored than I actually need, so I just took out a few of them. Still must make sure, the codes are correct.
So here goes he model:
class ReturnHeader < AciveRecord::Base
##return_reason_keys = {"010" => "Wrong Produc",
"DAM" => "Damaged",
"AMT" => "Wrong Amount"}
def self.return_reason_select
##return_reason_keys.invert
end
def return_reason
##return_reason_keys[nav_return_reason_code]
end
end
Model contains more code of course, but that's the part that matters. Relevant here is, that keys in the hash are strings, not symbols.
In the views i use it like this:
In the form for edit:
<%= form_for #return_header do |form| %>
<%= form.label :nav_return_reason_code "Return Reason" %>
<%= form.select :nav_return_reason_code, options_for_select(ReturnHeader.return_reason_select, #return_header.nav_return_reason_code) %>
<% end %>
(Maybe no the most elegant way to do it, but works. Don't know, why options_for_select expects a hash to be "text" => "key", but that's the reason, why above class level method returns the hash inverted.)
In my index action the return reason is listed in one of the columns. There I can get the value simply by
#return_headers.each do |rh|
rh.return_reason
end
If you have trouble to get it run, check that keys a correct type and value. Maybe add some debug info with logger.info in the methods to see what actual data is used there.

Resources