Rails form inputs using context validation - ruby-on-rails

In my app I have a validation rule like this
validates_presence_of :name, :on => :custom_context
When I'm saving my data I use
#obj.save(:context => :custom_context)
So that my context validation rule is applied. This works fine. By in my form, the name field is not marked with asterisk. How can I tell my form helper that we are in the :custom_context context and the name field must be marked as required?

I did not understand what you are trying to do BUT understood the scenario.
You can use an attribute_accessor in your model say -
attribute_accessor :context
In your view(.html.erb file) do the following inside your <% form_for %>
<%= f.hidden_field :context, :value => "custom_context" %>
And in your model :
validates_presence_of :name, :if => Proc.new { |variable|
variable.context == "custom_context"}
I think this should help :D

OK, I guess there is no perfect way to do this. Eventually I did something like this:
<%= f.input :name, :required => required_in_context?(:name, :custom_context) %>
And I wrote a helper method:
def required_in_context? field, context
required = false
MyClass.validators.each do |v|
required = true if v.kind == :presence && v.attributes.include?(field) && v.options == {:on => context}
end
required
end

Related

validates :terms, acceptance: true not showing error

In my model I have the following validator:
validates :terms, acceptance: true, on: :create, allow_nil: false
attr_accessor :terms
and in my form I have:
= simple_form_for #reservation do |f|
= f.error_notification
= f.input :terms, as: :boolean
The problem is that when user not accept the terms it not showing any error, why?
Try this:
validates :terms, :acceptance => {:accept => true} , on: :create, allow_nil: false
Problem may have terms as an actual column in the table. In general validates_acceptance_of is used without such a column, in which case it defines an attribute accessor and uses that for its validation.
In order for validates_acceptance_of to work when it maps to a real table column it is necessary to pass the :accept option, like:
validates :terms, :acceptance => {:accept => true} , :on => :create , allow_nil: false
The reason for this has to do with typecasting in Active Record. When the named attribute actually exists, AR performs typecasting based on the database column type. In most cases the acceptance column will be defined as a boolean and so model_object.terms will return true or false.
When there's no such column attr_accessor :terms simply returns the value passed in to the model object from the params hash which will normally be "1" from a checkbox.
Via noodl
I may have had a similar problem (Rails 4.2.0). I created a checkbox, but it would be ignored and never report and error if unchecked. I found that adding the parameter to the .permit part of my Strong Parameters allowed it to be present.
In my view template for my _form I have something like this:
<div class="field">
<%= label_tag :tos, 'I accepts the TOS' %><br>
<%= f.check_box :tos %>
</div>
I generated my model using scaffold, so my create method start like this
def create
#thing = Thing.new(thing_params)
then near the bottom I have the following for thing_params
def thing_params
params.require(:thing).permit(:field1, :field2, :tos)
end
in my model I used the following:
validates_acceptance_of :tos
If I leave out ':toslike thisparams.require(:thing).permit(:field1, :field2) it will not pop up an error and allows it to continue. This seems counter-intuitive because if Strong Parameters is removing the :tos field then I would think the validate_acceptance would fail.
I had initially just create a checkbox without using f.check_box. Now, if I even try to call the new route without :tos" being listed as permitted, rails throws an error. There also seems to be some rails magic going on because if I remove the validates_acceptance_of from my model, I receive an NoMethodError error when rendering my view saying undefined methodtos'` for the line
<%= f.check_box :tos %>
Would be great if someone else could explain what exactly is going on as I just hacked this together from googling and guessing.

Checking presence of fields in form which don't exist in the model

My Server model:
json_field
I have some multiple fields in my create form and I store them as one json(blod) field in my Server model.
So, I cannot use:
validates_presence_of :json_field, :message => "can't be blank"
Imagine this is my view:
= simple_form_for(#server, :html => one_submit_only(:server), :remote => true) do |f|
= link_to_close "servers/new"
= f.error_messages :object_name => t('server')
%input{:type=>"text", :name=>"page_title"}
%input{:type=>"text", :name=>"page_body"}
How could I show the error message when one of those fields(page_title or page_body) in the form is blank?
Can't you just check the fields as they appear in the form:
validates :page_title, :page_body, presence: true

Simple_form always sets pattern for email input element

I use simple_form to display an email field:
= f.simple_fields_for :user do |f|
= f.input :email, wrapper: :append, pattern: false do
= f.input_field :email, type: "email"
Somehow it always set's the pattern for the input field but I want to use the HTML5 validation of the email input field instead.
Is there a way to prevent simpleform from setting the pattern?
You can monkey-patch SimpleForm:
module SimpleForm
module Components
module Pattern
def pattern
# Deactivated for good:
#input_html_options[:pattern] ||= pattern_source
nil
end
end
end
end
Now, pattern will no longer be generated from validations. You need to add pattern manually to each input that requires a pattern. Example:
= f.input :name, pattern: "[a-zA-Z]+"
Had the same problem ... found a workaround but its kind of hacky
add
:pattern => ".*"
to your field like this
<%= f.input_field :email, :autofocus => true, :pattern => ".*", :class => "span12", :placeholder => t('placeholder.email') %>
I would rather make a string_input.rb file in the app directory some where ... may be in a folder named 'inputs' then have this code
class StringInput < SimpleForm::Inputs::StringInput
def input
input_html_options[:class] = "input-xlarge #{input_html_options[:class]}"
unless string?
input_html_classes.unshift("string")
input_html_options[:type] ||= input_type if html5?
end
input_html_options[:pattern] = nil
add_size!
#builder.text_field(attribute_name, input_html_options)
end
end
This will have effect on all the pattern attributes so no need to put it in explicitly. of course you can also add a condition if the input type is email or not if you want to specify.
Happy coding :)

Deleting a Paperclip Attachment in Activeadmin

I'm using paperclip to add image attachments to several models and Activeadmin to provide a simple admin interface.
I have this code in my activeadmin model file which allows for image uploads:
form :html => { :enctype => "multipart/form-data"} do |f|
f.inputs "Details" do
f.input :name
f.input :subdomain
end
f.inputs "General Customisation" do
f.input :standalone_background, :hint => (("current image:<br/>").html_safe + f.template.image_tag(f.object.standalone_background.url(:thumb))).html_safe, :as => :file
end
end
which works fine. All of the images I'm attaching like this are optional and so I'd like to give the user the option to remove a previously added image but can't work out how to do this in Activeadmin. All of the example I've seen are for situations where the attachments are managed through a separate has_many association rather than being part of the main model.
Does anyone know of a way to do this?
In your active admin view
form :html => { :enctype => "multipart/form-data"} do |f|
f.inputs "Details" do
f.input :name
f.input :subdomain
end
f.inputs "General Customisation" do
f.input :standalone_background, :hint => (("current image:<br/>").html_safe + f.template.image_tag(f.object.standalone_background.url(:thumb))).html_safe, :as => :file
f.input :remove_standalone_background, as: :boolean, required: false, label: "remove standalone background"
end
end
In your model
You could define a status flag like bellow
attr_writer :remove_standalone_background
def remove_standalone_background
#remove_standalone_background || false
end
OR (depreciated in rails 3.2)
attr_accessor_with_default : standalone_background,false
before_save :before_save_callback
And
def before_save_callback
if self.remove_standalone_background
self.remove_standalone_background=nil
end
end
You could implement this by creating a custom method. This can be done
member_action :custom_action, :method => :get do
//code
end
Also you should add a custom column with a link such as
index do
column "Custom" do |item|
link_to "Custom action", "/admin/items/custom_action"
end
end
Another option is to have a status flag for the attachment or image. Before saving the edited object, you unlink the image.
Thank you for your help guys. This is the final working code...
admin/product.rb
f.input :image, required: false, hint: (("Current image:<br/>").html_safe + f.template.image_tag(f.object.image.url(:thumb))).html_safe
f.input :remove_image, as: :boolean, required: false, label: "Remove Image"
models/product.rb
attr_writer :remove_image
def remove_image
#remove_image || false
end
before_validation { self.image.clear if self.remove_image == '1' }
Although accepts_nested_attributes_for(:foo, allow_destroy: true) only works with ActiveRecord associations like belongs_to we can borrow from its design to have paperclip attachment deletion work in a similar way.
(To understand how nested attributes work in Rails see http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html).
Add a <attachment_name>_attributes= writer method like below to your model that already uses has_attached_file:
has_attached_file :standalone_background
def standalone_background_attributes=(attributes)
# Marks the attachment for destruction on next save,
# if the attributes hash contains a _destroy flag
# and a new file was not uploaded at the same time:
if has_destroy_flag?(attributes) && !standalone_background.dirty?
standalone_background.clear
end
end
The <attachment_name>_attributes= method calls Paperclip::Attachment#clear to mark the attachment for destruction when the model is next saved.
Next open the existing app/admin/your_model_here.rb file (use the correct file path for your app) and setup strong parameters to permit the _destroy flag nested attribute on <attachment_name>_attributes:
ActiveAdmin.register YourModelHere do
permit_params :name, :subdomain,
:standalone_background,
standalone_background_attributes: [:_destroy]
In the same file, add a nested _destroy checkbox to the ActiveAdmin form block. This checkbox must be nested within <attachment_name>_attributes using semantic_fields_for (or one of the other nested attributes methods provided by formtastic).
form :html => { :enctype => "multipart/form-data"} do |f|
f.inputs "Details" do
...
end
f.inputs "General Customisation" do
...
if f.object.standalone_background.present?
f.semantic_fields_for :standalone_background_attributes do |fields|
fields.input :_destroy, as: :boolean, label: 'Delete?'
end
end
end
end
Your form should now show a delete checkbox when there is an attachment present. Checking this checkbox and submitting a valid form ought to delete the attachment.
Source: https://github.com/activeadmin/activeadmin/wiki/Deleting-Paperclip-Attachments-with-ActiveAdmin

add checkbox with simple_form without association with model?

How I can add checkbox with simple_form without association with model?
I want to create checkbox which will handle some javascript events, but don't know?
Maybe I miss something in documentation?
Want't to use similar like following:
= simple_form_for(resource, as: resource_name, url: session_url(resource_name), wrapper: :inline) do |f|
.inputs
= f.input :email, required: false, autofocus: true
= f.input :password, required: false
= f.input :remember_me, as: :boolean if devise_mapping.rememberable?
= my_checkbox, 'some text'
You can add a custom attribute to the model:
class Resource < ActiveRecord::Base
attr_accessor :custom_field
end
Then use this field as block:
= f.input :custom_field, :label => false do
= check_box_tag :some_name
Try to find "Wrapping Rails Form Helpers" in their documentation https://github.com/plataformatec/simple_form
You could use
f.input :field_name, as: :boolean
The command proposed by huoxito does not work (at least not in Rails 4). As far as I figured out the error is caused by Rails trying to look up the default value for :custom_field, but because this field does not exists this lookup fails and an exception is thrown.
However, it works if you specify a default value for the field using the :input_html parameter, e.g. like this:
= f.input :custom_field, :as => :boolean, :input_html => { :checked => "checked" }
This question comes first on google with no appropriate answer.
Since Simple Form 3.1.0.rc1 there is a proper way of doing it explained on the wiki: https://github.com/plataformatec/simple_form/wiki/Create-a-fake-input-that-does-NOT-read-attributes
app/inputs/fake_input.rb:
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
template.text_field_tag(attribute_name, nil, merged_input_options)
end
end
Then you can do <%= f.input :thing, as: :fake %>
For this specific question you have to change the second line of the method to:
template.check_box_tag(attribute_name, nil, merged_input_options)
For versions prior 3.1.0.rc1 admgc gave a solution that is adding the missing method merge_wrapper_options:
https://stackoverflow.com/a/26331237/2055246
Add this to app/inputs/arbitrary_boolean_input.rb:
class ArbitraryBooleanInput < SimpleForm::Inputs::BooleanInput
def input(wrapper_options = nil)
tag_name = "#{#builder.object_name}[#{attribute_name}]"
template.check_box_tag(tag_name, options['value'] || 1, options['checked'], options)
end
end
then use it in your views like:
= simple_form_for(#some_object, remote: true, method: :put) do |f|
= f.simple_fields_for #some_object.some_nested_object do |nested_f|
= nested_f.input :some_param, as: :arbitrary_boolean
i.e. The above implementation supports nested fields correctly. Other solutions I've seen do not.
Note: This example is HAML.
Here is another variation:
= f.label :some_param, class: "label__class" do
= f.input_field :some_param, class: "checkbox__class"
Label text

Resources