Casting form inputs in a Rails form - ruby-on-rails

I seem to have a few cases where I want to accept an array of values via a form:
<input name="model[field][]>
<input name="model[field][]>
<input name="model[field][]>
But then on the server side, my "Model.field" attribute is implemented as a string.
So there will be some kind of transformation taking the array and converting it into a string.
My question is this: Is there a way to generically handle this.
On the server side, is there a way where I can say "Whenever the form tries to assign an array to a string, use function X to do the assignment"
I realise my schema is questionable... and it's questionable to do blanket casting.... but if I HAD to do this, what would be the best method?
thanks!

It's Rails. You can do everything you want. If I understand you correctly: You receive an array from form and you need to save this array in database. Here are steps to do this
1) permit this attribute (you are using Rails4). Do smth like this:
def model_params
params.require(:model).permit(:title, {:field => []})
end
2) Serialize field in database. Just put this in your model:
class Model
serialize :field
end
Rails will handle all for you. Read more about serialize method

Related

Strong parameters for array of records

Strong parameters has me very confused. I'm writing a form to create several records at once. They are passed in params as an array of attributes:
{ :appointments => [ { :field1 => 'value1'
, :field2 => 'value2'
}
, # next record
]
}
Then in the controller I would like to do something like
params[:appointments].each do |a|
app = Appointment.create! a
end
But I run into lots of trouble with strong parameters, in the form of ForbiddenAttributeErrors. I've tried using appointment_params and whitelisting attributes, but with no luck. I can't find any good documentation matching my use case. They all assume the array of records should be nested below some owner record but this is not the case here.
Any help would be appreciated.
Make sure you are white listing your array in addition to the actual model attributes.
It seems like you have used the scaffolded version of the params.require method and not have updated that method when you changed your controller to deal with an array of appointments rather than one appointment at a time.
Something like this should work:
params.require(:appointment).permit(:field1, :field2, appointments: [:field1, field2])
or
params.require(:appointments).permit(:field1, :field2)
Not sure exactly what the rest of yoru code looks like, but it seems like you're not permitting the array itself, the above code samples attempt to white list what I would assume that attribute might be named.
If you are only using the attributes to create a new Appointment record, you can use the following
Appointment.create!(params.permit(applications: [:field1, :field2])[:applications])
If you really want to iterate over the array, you can do
params[:appointments].each do |a|
app = Appointment.create!(a.permit(:field1, :field2))
end

render form with json data using postgre hstore

I have a Attributes column in one of my model,
and the Attributes column is hstore type, it stores JSON format string,
I want my user to edit it in normal form type,
I wish it could span to 4 input-fields automatically,
name
range
security
default_value
is there any gem to achieve it ?
UPDATE
I followed #zwippie solution, and show it in the form correctly,
But it can not be updated,
When I tried to updated it,
But I should add those fields into strong parameters,
is there any dynamic way, to achieve this.
To expand all the columns in the hstro rather than hard code store_accessor :cgi_attributes, :name, :range, :security, :default_value,
and also append it into white list
= f.text_area column, class: 'input-xxlarge'
But it
Rails 4 can do this out of the box. Add this to your model:
store_accessor :attributes, :name, :range, :security, :default_value
Now you can use the hstore attributes as normal Active Record attributes:
item = Item.find(2)
item.update(security: '5/6')
Or in a form:
<%= f.text_field :default_value %>
I dont think theres a gem to achive it but its still possible using metaprogamming.
Using metaprogramming you can create methods dynamicly based on the data of an object. I explained it here for serialized data which is very likely to your situation.
The thing you need to extend is the view but thats not such a problem you can simpley call a methed that returns all keys of the serialized data and then loop through them and generate inputs.
I guess PostgreSQL hstore might be useful if your are using a postgresql database in your application.

serialize & before_save in Rails 4

I have a DocumentType model w/ a extensions attribute. In my form I'm allowing people to insert those extensions into the form.
I want to be able to parse that input before saving, stripping out any invalid options, convert it into an array and have Rails serialize it.
I have the following code but I just end up w/ the input that the user gave in the form instead of an array:
class DocumentType < ActiveRecord::Base
serialize :extensions
before_save :process_extensions
def process_extensions
self.extensions = [*self.extensions.gsub(/[^a-z ]+/i, '').split(' ')].uniq
end
end
The key to understanding what's happening is knowing when serialization occurs. By inspecting serialization.rb in activerecord you'll see that the serialization magic happens by overriding type_cast_attribute_for_write, which is called on write_attribute. That is, on attribute assignment. So when you do:
document_type.extensions = something
something gets serialized and written to the extensions attribute. That is way before the save takes place. In fact, you don't even have to call save on document_type to have the attribute serialized.
The best workaround I know is to override extensions= on DocumentType. Something like:
def extensions=(value)
value = [*value.gsub(/[^a-z ]+/i, '').split(' ')].uniq
write_attribute :extensions, value
end
I believe this append because the value of extensions is serialized while the model is validated by Rails, and your process_extensions method is called later (before the model is saved) and does not act as expected
Try to use before_validate instead
before_validate :process_extensions

Saving an hash as a string, reform it later

I want to be able to save a params[:object] hash in a text field of a drafts table, to keep this hash saved and pull it out later. I want to know if it's possible to do so that I get a hash out later, with functionality.
Right now, when I am saving a post in here, so I have the following line:
#draft = user.drafts.build(:content => params[:post])
This saves the params[:post] hash as:
"--- !map:ActiveSupport::HashWithIndifferentAccess \ntitle: asdfasdfasdf\ncontent: \"\"\ndiscussion_id: \"87\"\ndraft: \"false\"\n"
If I find the draft (ie d = Draft.find(1)) and then I try to pull this hash out, I just get the string value here.
Is there some way I can reform this into a hash? Or is there a better way to go about saving the params hash in the first place?
Thanks
Check out the class method serialize in ActiveRecord::Base:
class Draft < ActiveRecord::Base
serialize :content, Hash
end
It should save a fair amount of time over doing this yourself!

How to Inspect Validations that exist on ActiveRecord::Base Objects

I want to create a helper method to automatically output text_fields with a :maxlength attribute. I want this maxlegth to be set based on the :max specified in the field attributes validates_length validation, so as to remain DRY.
My question is: Is there a good way to inspect the validations that are present on an objects attribute, and then extract the maximum length from that validation.
I also plan on extracting other things like the regex from validates_format so I can set an attribute on the text_field, which can then be used by js to run client side validations.
Thanks.
Bonus points: Why doesn't Rails Automatically add the maxlength to text fields for us?
In Rails 3 you can call the _validators method on an object to get the list of validators that will run:
t = Ticket.new
t._validators
Well, I don't know if this is a 'good' way, but you can initialize a new object, call #valid? on it and then call #errors to get a hash of attributes and error messages. Then you'd have to parse those errors messages.
user = User.new
user.valid?
errors_hash = user.errors

Resources