JSONB: store_model gem. Modify content on posting data - ruby-on-rails

I'm using StoreModel gem to wrap my JSON-backed DB column setting with an ActiveModel-like classes. This is my model simplified model
class Entity < ApplicationRecord
attribute :settings, Setting.to_type # settings is a jsonb datatype in the database
end
class Setting
include StoreModel::Model
attribute :setting1, :boolean
attribute :setting2, :boolean
attribute :setting3, :boolean
end
For the form I have the following
<%= form_for #entity do |f| %>
<%= fields_for :settings, #entity.settings do |ff| %>
<%= ff.check_box :setting1 %>
<%= ff.check_box :setting2 %>
<% end %
<% end %>
For an existing record, this would overwrite all values in the settings attribute of my Entity model, hence it would set setting3 no null (which is not passed by the form / params)! How can I submit values to keep existing values and just modify ones I submit.

It was discussed in the GitHub issue earlier, TLDR it's not possible out–of–the–box, but there is a workaround.

Related

Simple form remove associated record

I'm using simple_form and I would give user ability to quickly remove an associated record. (eg. "checking/uncheking")
How is it possibile with simple_form? Is there another gem to help with this?
Parent has many children
<%= simple_form_for #parent do |f| %>
<%= f.simple_fields_for :childens do |p| %>
<%= p.input :title, as: :boolean %>
<% end %>
<% end %>
Rails 5.2
You don't need another gem for that. There are several things you need to do:
Add allow_destroy: true to the accepts_nested_attributes_for :children in the parent model
Add a <%= p.input :_destroy, as: :boolean %> to the nested form
Whitelist the _destroy pseudo attribute in your controller by listing it in children_attributes in the permit call
Essentially this is a feature of Rails' accepts_nested_attributes_for - it sets up the children_attributes setter to not only create/update associated records but also delete them in the presence of _destroy in the passed hash.

How to store defined attributes that will be used in an Hstore in Rails?

One of the attributes on my Template model is called settings and is an HSTORE type.
I have the following right now:
# Template model
store_accessor :settings, :width, :height,
:number_of_allowed_images, :is_ecommerce
# View
<%= f.fields_for :settings do |s| %>
<%= f.text_field :width %>
<%= f.text_field :height %>
<%= f.check_box :is_ecommerce %>
<%= end %>
# Controller
params.require(:template).permit(:name, settings: [:width, :height, :number_of_allowed_images, :is_ecommerce])
I have just shown those 4 settings attributes, but I have around 80. Some of those attributes have to be treated as a text_field, but others are boolean (is_ecommerce) for example.
Right now I am writing 'statically' the attributes in both the model, view and controller, but I am looking for a way that I can manage (add/remove/delete) the attributes that are available for the settings, taking into account that not all the attributes are of the same type, and that ideally I want some of them to have a default value (is_ecommerce should be false by default).
How do you do these when dealing with HSTORE attributes?

Why this error in Three Level nested attributes in Rails 3?

this error not display on creation of record
only come when update
please help..
Your code in edit template seems to be the source of this problem. You must have defined member's last_name without using the fields_for object.
<%= f.fields_for :member do |member| %>
<%= member.text_field :first_name %>
... # Other attributes
<%= member.text_field :last_name %> # make sure you are using the fields_for instance (member in this example) here.
<% end %>
add attribute to attr_accessible list like
attr_accessible attr1, attr2
this is rails 3 technique to avoid unwanted attribute assignments like sensitive data from form.
Is applicable while using object.update_attributes function and note with object.save.

How to access nested child element in fields_for

I'm trying to access a Hash type of mongoid in fieds_for and I already have a relationship with a model and want to access a hash of that model. Something like:
class Leave
field :leaves_types, :type => Hash
end
class User
has_many :leaves
end
Want to do something like:
form_for #user do |f|
f.fields_for :leaves.leave_types...
How I can achieve this? Thanks in advance.
You should give a block to fields_for. For more information on that method see docs. In your case, first, add this line to your User model:
class User
has_many :leaves
accepts_nested_attributes_for :leaves
end
This is required so that when your form is posted, the attributes coming from the form fields for leaves via params were handled correctly.
Now your template should look like this (for simplicity by now I assume that your Leave also has a simple text field named foo):
<%= form_for #user do |f| %>
...
<%= f.fields_for :leaves do |leave_fields| %>
# Fields for a leave here ----v
Foo: <%= leaves_fields.text_field :foo %>
<% end %>
<% end %>
Or, if you #user.leaves already initialized and you want form builder to put its values to form fields, you have to iterate over #user.leaves, passing each of them to fields_for as second argument:
<%= form_for #user do |f| %>
...
<% #user.leaves.each do |leave| %>
<%= f.fields_for :leaves, leave do |leave_fields| %>
# Note the addition here --^
Foo: <%= leave_fields.text_field :foo %>
<% end %>
<% end %>
<% end %>
But your question has another one inside: you have not a text field, but a hash, and there is no default form input for it (i.e. there is no f.hash_field :leaves_types). So you may want to make it by yourself like suggested in these questions: [1], [2] and [3].
Anyway, having a Hash field seems rather uncommon to me, so maybe Hash can be somehow replaced, say, with another has_many association (not sure), and in this case you will only need another nested fields_for.

rails simple_form fields not related to the model

I have an existing form which is tied to a model named 'Order', but i want to add new form fields that will capture Credit Card info such as name, cc number, etc to be processed on a 3rd party payment gateway.
But since i don't want to save CC info in our database, there are no corresponding columns of that in my order table. And this gives me an error when submitting the form that those Credit card input fields are not 'part' of the order model.
If I understand your answer correctly, what you want to do is explained in the official wiki page here: Create a fake input that does NOT read attributes. You can use a field not related to any real database column by Edward's suggestion, however you don't need to define an attribute in your model if the form field is nothing to do with the model.
In summary, the trick explained in the page is defining a custom input called 'FakeInput' and use it like this:
<%= simple_form_for #user do |f| %>
<%= f.input :agreement, as: :fake %>
....
Do not forget to restart your rails server after adding/modifying a custom input as Fitter Man commented.
UPDATE: Please note that the official wiki page has updated and the sample code on the wiki page is not working for those which use older versions of SimpleForm. Use code below instead if you encounter an error like undefined method merge_wrapper_options for.... I'm using 3.0.1 and this code works well.
class FakeInput < SimpleForm::Inputs::StringInput
# This method only create a basic input without reading any value from object
def input
template.text_field_tag(attribute_name, input_options.delete(:value), input_html_options)
end
end
You can use attr_accessor
class Order < ActiveRecord::Base
attr_accessor :card_number
end
Now you can do Order.first.card_number = '54421542122' or use it in your form or whatever else you need to do.
See here for ruby docs http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr_accessor
and here for a useful stackoverflow question What is attr_accessor in Ruby?
Don't get it mixed up with attr_accessible! Difference between attr_accessor and attr_accessible
The best way to handle this is to use simple_fields_for like so:
<%= simple_form_for #user do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.input :email %>
<%= simple_fields_for :other do |o| %>
<%= o.input :change_password, as: :boolean, label: 'I want to change my password' %>
<% end %>
<% end %>
In this example, I have added a new field called change_password which is not part of the underlying user model.
The reason this is a good approach, is that it lets you use any of the simple form inputs / wrappers as fields. I don't care for the answer by #baxang, because it doesn't allow you to use different types of inputs. This seems more flexible.
Notice though for this to work, I had to pass :other to simple_fields_for. You can pass any string/symbol as long as there is not a model with that same name.
I.e. unfortunately I can't pass :user, as simple_form would try to instantiate a User model, and we'd get the same error message again...
Also if you're just trying to add something and get it into the params, but leaving it out of the model's hash, you could just do FormTagHelpers. http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html
Example:
<%= simple_form_for resource, :as => resource_name, :url => invitation_path(resource_name), :html => {:method => :post} do |f| %>
<%= devise_error_messages! %>
<% resource.class.invite_key_fields.each do |field| -%>
<%= f.input field %>
<%= hidden_field_tag :object_name, #object.class.name %>
<%= hidden_field_tag :object_id, #object.id %>
<% end -%>
I found a very simple (and somewhat strange) workaround.
Just add the input_html option with any value key inside. E.g:
= simple_form_for #user do |f|
= f.input :whatever, input_html: {value: ''}
Tested simple_from versions: 3.2.1, 3.5.1

Resources