Adding a hidden input field to all the forms in rails - ruby-on-rails

Is there a way to add a hidden input field to all the form that are declared in views in rails. The hidden field needs to have a value passed by a public property in the controller. Essentially I want any form that is given as response by the web server to have an additional hidden input element.
How I do this? Can I override the form_for by some means ? Or can I go with a wrapper of form in partials and enforce everybody to use the partial?

EDIT: OK, my first pass on this didn't work because you can't define a value in an initializer that you're later going to pass in from a controller. So you can go about this one of two ways.
You can define a CustomFormBuilder class - put it in an initializer -
class CustomFormBuilder < ActionView::Helpers::FormBuilder
def submit(value, options = {})
#template.hidden_field_tag(options.delete(:custom_param)) + super
end
end
Then pass the :builder option to form_for
form_for #whatever, builder: CustomFormBuilder
and assuming you call submit(value, options) in the form, where options includes your custom_param, it will be overwritten by the custom method that inserts your hidden field with the value you want.
The alternative is to monkey patch monkey patch FormTagHelper instead:
module ActionView::Helpers::FormTagHelper
def extra_tags_for_form_with_custom_param(html_options)
hidden_field_tag(html_options.delete('custom_param') +
extra_tags_for_form_without_custom_param(html_options)
end
alias_method_chain :extra_tags_for_form, :custom_param
end
That's tweaking the code form_for uses to insert the authenticity token at the top of every form. Now you can pass that custom param to form_for after setting it as an instance variable in the controller:
form_for #object, custom_param: #custom_param do |f|
If you are using Ruby 2.0+ then you can use module prepend instead of the deprecated alias_method_chain e.g.
module CustomParamFormPatch
private
def extra_tags_for_form(html_options)
hidden_field_tag(html_options.delete('custom_param')) +
super
end
end
ActionView::Base.prepend(CustomParamFormPatch)

Related

Unpermitted parameter error when adding request parameter while using Administrate

I'm using Administrate v0.11.0 with search_term textbox,
it works totally fine,
and now I want to add a request parameter my_search_condition_flag which is a boolean flag value that affects search condition.
In my index action of controller,
I added the following line, so that requests with this parameter pass the Strong Parameters validation.
params.permit(:search, :my_search_condition_flag)
The rest of the code in index action is simply copied from ApplicationController.rb of Administrate.
When I make a HTTP request with request parameter my_search_condition_flag=1 ,
my index action is processed just fine,
but HTTP response returns following error:
ActionController::UnpermittedParameters in Admin::MyPage#index
Showing /usr/local/bundle/gems/administrate-0.11.0/app/views/administrate/application/_search.html.erb where line #19 raised:
found unpermitted parameter: :my_search_condition_flag
which is raised from rendering method of search_term textbox inside index.html.erb
<% if show_search_bar %>
<%= render(
"search",
search_term: search_term,
resource_name: display_resource_name(page.resource_name)
) %>
<% end %>
I've already tried the following to my Dashboard class, introduced here:
# -- Overwrite the method to add one more to the permit list
def permitted_attributes
super + [:my_search_condition_flag] # -- Adding our now removed field to thepermitted list
end
How can I tell Administrate to permit a parameter which I want to add?
Do I have to use request body instead? (which I don't want)
You were on the right track there. The exception originates at /app/views/administrate/application/_search.html.erb:19, as you mention. If you look there, you'll see it uses the method clear_search_params, which also uses strong_parameters to allow/deny query params. You can override this with a helper of your own. For example:
module Admin
module ApplicationHelper
def clear_search_params
params.except(:search, :page, :my_required_condition_flag).permit(
:per_page, resource_name => %i[order direction]
)
end
end
end
If you do this, you'll get a new, related error. This time from /app/helpers/administrate/application_helper.rb:48. The method there is called sanitized_order_params, and can be overriden similarly:
module Admin
module ApplicationHelper
# ...
def sanitized_order_params(page, current_field_name)
collection_names = page.item_includes + [current_field_name]
association_params = collection_names.map do |assoc_name|
{ assoc_name => %i[order direction page per_page] }
end
params.permit(:search, :my_required_condition_flag, :id, :page, :per_page, association_params)
end
end
end
And with that, you should be clear of errors.
Admittedly, this is not very nice fix. Ideally Administrate should be providing some better way to override this list of allowed search params. Fancy submitting a PR? ;-)

Calling Rails helper from rabl Rails

I'm trying to call a Rails helper from my rabl template, but it's not working.
I have an image object and I want to pass its pass through my api. So then image path should have the full url (Ex: http://<my url>/uploads/myimage.jpg). The host part I want to add through a Rails helper.
#my rabl file
object #image
attributes :id, :picture_url
What I want is something like (I tried this but doesn't work)
#my rabl file
object #image
attributes :id, full_url(:picture_url)
#app/helpers/application_helper.rb
module ApplicationHelper
def full_url(path)
"#{request.host}#{path}" #something like this
end
end
But when I do this, it completely drop the :picture_url attribute from the json object
I believe this might work for you:
object #image
attributes :id
node(:picture_url) { full_url(:picture_url) }
node allows you to define things that do not directly map to object attributes.
EDIT 1
It looks like I got the node syntax wrong. Try this instead:
node(:picture_url) { |i| full_url(i.picture_url) }
By calling full_url(:picture_url), attributes gets a string that doesn't correspond to a method on the #image object, i.e., #image.id returns something, while #image.http://... isn't even valid :).
I would suggest you leave the view as it was first, and set the value of the #image.picture_url using your helper in the controller.

Affecting resulting params set from form_for

Relative newbie here to Ruby on Rails.
Using the standard form_for method in the view for my SomeobjController#new action
= form_for #someobj do |f|
.
.
.
%p.submits
= f.submit "Submit", :class => "submit"
a submission param[] array is produced that contains a hash of #someobj for all the fields set in the form, such that
param[someobj] => { "field1" => "val1", "field2" => "val2", ... }
I would prefer to put a different value, the result of someobj.to_s to param[someobj] for the SomeobjController#create to work with, such that
param[someobj] => "strvalfromtos"
I doubt it's relative, but just in case, the model underlying this #new action is not persistent in the database (i.e., Someobj is not derived from ActiveRecord::Base, though some portions of ActiveModel are included.)
I haven't had luck trying to adjust this until after #create is invoked, but its the submission from #new to #create that I want to amend. It's not clear to me if I should be focusing more on the form_for statement or doing something special in the controller (I'm guessing the form_for is the right focus).
And, yes, this whole thing is probably a bit OCD of me, but the actual fieldnames are long (appropriately for the model) but data needed by #create is very small.
Is there a relatively painless way to do this, assuming that someobj.to_s has already been written?
Many thanks,
Richard
Change
When you submit the form, your controller will receive the params hash, as you've stated (Rails params explained?)
That means you can change any value in the hash that you wish:
def create
#has access to the params hash
params[:owner][:key] = value
end
As the create method receives the hash object, you'll have to change it in here. But because it's a standard hash (which has already been declared), you should be able to alter it as required
Add
If you want to add values to the params hash, you can use the .merge method, like this:
def create
#has access to the params hash
params[:key].merge(user_id: current_user.id)
end

Rails: Proper way to add functionality to rails methods

I'm just starting to tinker with extending the rails framework, and as an experiment, I thought I'd add some extra info inside the form_for helper. Specifically, when form_for is called, I'd like to generate an extra h1 tag such as:
# regular form_for <form> opening tag
<h1>Woohoo! It's added!</h1>
# tags fed into form_for via &proc
# form_for close <form> tag
At the moment I've added a /lib file that opens up ActiveRecord::FormHelper and overrides "form for". Needless to say writing out the whole form_for method with just the one added line added is dog ugly...but I can't call super() because, well, instead of inheriting from the method I'd like to super(), I've just overwritten it in /lib.
So, assuming I stubbornly want the functionality to be called via the same form_for tag (instead of, for example extended_form_for), what's the standard way for calling back to the original form_for method I'm overwriting? alias_method_chain? Thought I'd ask before I cement in some potentially lousy practices. If any hardened veterans could give an example I'd be appreciative.
Cheers
You could override form_for in your ApplicationHelper:
module ApplicationHelper
def form_for(*)
content_tag(:h1, "Woohoo! It's added!") + super
end
end
alias_method_chain is by far the simplest way to overwrite the method while still being able to call the original method. So in your lib file you'll want something like this:
def form_for_with_header(...)
form_for_without_header(...)
content_tag(:h1, "Header tag here")
# etc...
end

Automatically append parameters to *_url or *_path methods (Rails)

I have a particular set of views relating to one of my controllers, whereby I want any call to *_path or *_url to append a set of parameters.
Is there some magic method I can override that will let me do this? I have no idea where in the Rails code the *_path or *_url methods are even handled.
Edit for clarity: I'm looking for a way to do this such that I don't have to modify every link in every view where this needs to occur. I don't want every coder who touches this set of views to have to remember to append a parameter to every link they add to the page. The same parameter should be appended automatically. I consider a change to the *_url or *_path call a failure. Similarly, having to override every *_url or *_path call is considered a failure since a new method would have to be added/removed whenever a new link is added/removed.
You can do this by overriding url_for since all the routing methods call it.
module ApplicationHelper
def url_for(options = {})
options.reverse_merge!(#extra_url_for_options) if #extra_url_for_options
super
end
end
Now all you need to do is use a before_filter to set #extra_url_for_options to a hash to force all urls.
class MyController < ApplicationController
before_filter do { #extra_url_for_options = { :format => 'html' } }
end
Note that this will force all links to use the extra options.
Thanks to Samuel's answer, I was able to create a final working solution via a new helper, which I've included below.
module ExampleHelper
def url_for(options={})
options = case options
when String
uri = Addressable::URI.new
uri.query_values = #hash_of_additional_params
options + (options.index('?').nil? ? '?' : '&') + uri.query
when Hash
options.reverse_merge(#hash_of_additional_params)
else
options
end
super
end
end
You can try to use the with_options method. In your view you can do something like
<% with_options :my_param => "my_value" do |append| -%>
<%= append.users_path(1) %>
<% end %>
Assuming you have the users_path of course. my_param=value will be appended to the url
You could make a helper method:
def my_path(p)
"#{p}_path all the parameters I want to append"
end
and in the view use
<%= eval(my_path(whatever)) %>
Eval with give you dynamic scope, so every variable available in your view can be used in the helper. If your parameters are constant you can get rid of eval calls.

Resources