Fill hstore attribute in Active Admin - ruby-on-rails

I have an hstore attribute named salary to store min and max values. To fill this in active admin (+ simple_form gem), I wrote the following:
ActiveAdmin.register Job do
permit_params :salary
form do |f|
f.simple_fields_for :salary do |salary|
salary.input :min, label: 'Min Salary'
salary.input :max, label: 'Max Salary'
end
f.actions
end
end
This shows me the right form with job[salary][min] and job[salary][max] inputs but doesn't save them when updated/created.
What am I doing wrong? Thanks.

hstore requires some special ActiveAdmin code to work on edit and creation. You will need to write a custom controller action. Something like:
ActiveAdmin.register Job do
controller do
def update
unless params[:job][:min].empty?
job = Job.find(params[:id])
job.min = params[:job][:min]
job.save
end
unless params[:job][:max].empty?
job = Job.find(params[:id])
job.max = params[:job][:max]
job.save
end
# Call the default update action
super
end
end
end
The above snippet is assuming that you are using something like
store_accessor :salary, :min, :max
to access your hstore properties on your Job model.

Related

Is it possible to prevent empty action text entries

I have a very simple action text model and form
class Course < ApplicationRecord
validates :title, presence: true
has_rich_text :content
end
<%= form_with model: #course do |f| %>
<%= f.text_field :title %>
<%= f.rich_text_area :content %>
<% end %>
It's all working great but since the content field is optional is it possible to create a course model without creating action_text_rich_texts entries that are empty/blank? Even if the user only enters the title without any content it's currently creating them and there's a lot of unnecessary and empty action_text_rich_texts rows in the database
The way I handled this in my application is with a before_save callback that removes the ActionText::RichText database record if the body is blank.
This avoids polluting the controller and works on both create and update actions. The body attribute of the action_text attribute is still accessible even without a corresponding database record, because ActionText will instantiate a new object if the record cannot be found (which allows you to test for blank? in either scenario).
Try this:
class Course < ApplicationRecord
validates :title, presence: true
has_rich_text :content
before_save :clean_up_content
private
def clean_up_content
self.content.destroy if self.content.body.blank?
end
end
I'm not sure about anything built into Actiontext for this, but I would imagine you could handle this at the controller level.
The first thing I would try is to see if not setting anything to content prevents Rails from creating an associated record:
class CourseController
def create
# remove course_params[:content] if it's blank
course_values = course_params[:content].blank? ? course_params.except(:content) : course_params
Course.create(course_values)
...
end
end
Extending Eric Powell's approach:
# app/models/concerns/do_not_save_blank_rich_text.rb
module DoNotSaveBlankRichText
extend ActiveSupport::Concern
included do
before_validation :do_not_save_blank_rich_text
end
private
def do_not_save_blank_rich_text
rich_text_attributes = self.class.reflections.values.select do |reflection|
reflection.options[:class_name] == "ActionText::RichText"
end.map(&:name)
rich_text_attributes.each do |rich_text_attribute|
if self.public_send(rich_text_attribute) && self.public_send(rich_text_attribute).body.blank?
self.public_send(rich_text_attribute).mark_for_destruction
end
end
end
end

Adding a custom JSONB text_field tag with simple_form

I have a JSONB type options column on a model in my Rails application and I am in a situation where I need to provide JSONB nested parameters manually for every single form. That's why the official solution proposed here is not an option.
So I might want to have something like:
options[first_name], options[last_name]
for one instance, and a totally different set for another:
options[pet_dog], options[frank_sinatra]
I assume it can well be implemented using something like
= f.text_field :options[pet_dog]
But it's not working.
My solution code looks like this:
View form:
= f.simple_fields_for :options do |p|
- #options.each do |o|
= p.input o.name.to_sym, input_html: {value: o.default_value}
Controller
def new
#options = Model.options
...
end
def create
#job = current_user.jobs.build(job_params)
end
...
def job_params
params.require(:job_request).permit(
...
options: get_options)
end
def get_options
some_model.options.pluck(:name).map(&:to_sym)
end
Possibly this will help someone.
How did you implement the jsonb column? Have you added this to the model?
serialize :options, HashSerializer
store_accessor :options, :pet_dog, :pet_cat, :pet_cow
*Note that HashSerializer is a class acting as a serializer, which looks like this:
class HashSerializer
def self.dump(hash)
hash.to_json
end
def self.load(hash)
(hash || {}).with_indifferent_access
end
end
What those 2 lines on the model do is allowing you using accessor (getter and setter) for the json/jsonb keys. So you can do this:
object = Model.new
object.pet_dog = 'Dog1'
object.pet_cat = 'Cat1'
object.options = {"pet_dog"=>"Dog1", "pet_cat"=>"Cat1"}
Which in turn, allows you to use it as form fields:
f.text_field :pet_dog
f.text_field :pet_cat
Do not forget to whitelist the attributes in the strong_params method
You can read more on this comprehensive post by Nando Vieira:
http://nandovieira.com/using-postgresql-and-jsonb-with-ruby-on-rails
Also, if you want a shortcut, there is a gem for this:
https://github.com/devmynd/jsonb_accessor

Rails - Create an object in database from nested params attributes

I have a rails application where the user creates a new job, and on the job/new _form i am having the user enter the information for the Company that the Job belongs too.
A job in the database is simply the Date it was created, the User who created it, and a additional foreign keys to tables like Company, Contact, Jobsite, etc.
A Company has_many jobs and a Job belongs_to a Company in my application.
So, before I can persist the Job into the database, I must first save the new Company so that I can then grab its primary key and put that in the Jobs company_id foreign key tuple.
I have everything coming back properly in the params hash like so:
>> params
=> {"utf8"=>"✓", "authenticity_token"=>"NiQO7Wlq87h3+YM//yIEMnctVKectfmZBb74suFOCcmg7g4YyCmGo2OiciOd3VuRDR52tKoE0v9nq1LYoTqHOQ==", "job"=>{"date"=>"2016-09-04 16:06:49 -0700", "user_id"=>"1", "company_id"=>"", "subcontractor_id"=>"", "jobsite_id"=>"", "companies"=>{"name"=>"Test Company", "phone"=>"(530)111-2222", "street"=>"3177 testing st", "city"=>"cameron park", "state"=>"ca", "zip"=>"95682", "email"=>"testemail#mail.com", "website"=>"testcompany.com"}}, "commit"=>"Create Job", "controller"=>"jobs", "action"=>"create"}
>>
And i can access the company information with params[:job][:companies]
So my theory is, in the Jobs controller, in def create, before i call #job.save, I must first do a #company.new(company_params) and #company.save so that I can save the company and then grab its primary key from the database.
But I need a little help with this.
Does anyone have some tips to give?
Edit:
This is a sample from my _form,
<%= f.fields_for #company do |company| %>
<div class="field">
<%= company.label :name %>
<%= company.text_field :name %>
</div>
Inside the Job Controller I have,
def new
#job = Job.new
#company = Company.new
end
def create
#job = Job.new(job_params)
#company = Company.new(params[:job][:company])
Job.rb has
accepts_nested_attributes_for :company
And the params are:
def job_params
params.require(:job).permit(:date, :user_id, :company_id, :subcontractor_id, :jobsite_id, :company_attributes => [:name, :phone, :street, :city, :state, :zip, :email, :website])
end
When i submit my form, the attributes come back as company instead of company_attributes, so it is telling me "Unpermitted parameter: company".
The way i have it set up, #job has not function to do #job.company.build, they are not nested that way. Company is its own table and a company can have many jobs, and all a job can do is perform #job.company
Which is why i need to create the company first so i can get its primary key and then say #job.company_id = #company.id
I hope this makes things more clear.
You could use nested_attributes_for :company or companies, looking at your code and seeing company_id, I'm assuming it's company.
=>{
...
"job"=>{
"date"=>"2016-09-04 16:06:49 -0700",
"user_id"=>"1", # <= Really shouldn't do this...
"company_attributes"=>{
"name"=>"Test Company",
"phone"=>"(530)111-2222",
"street"=>"3177 testing st",
"city"=>"cameron park",
"state"=>"ca", "zip"=>"95682",
"email"=>"testemail#mail.com",
"website"=>"testcompany.com"
}
},
...
}
Then in your permitted params you can do something like:
def job_params
params.require(:job).permit(:date, :company_id, :subcontractor_id, :company_attributes => [:name, :phone, :street, :city, :state, :email, :website])
end
You can then use fields_for :company in your form_tag. Also, add a blank company to the new job action.
def new
#job = Job.new
#job.build_company
end
EDIT
You can't use #company on feilds_for like this.
Perhaps fields_for :company, #company, but not just #company.
<%= f.fields_for :company do |company| %>
You have to use a symbol to represent the record's name.
Otherwise the fields name will be "job[company]" or in your case "job[companies]" instead of "job[company_attributes]".
And either way, you can just add the new objects to the new job's association instead of adding an instance variable.
#job.build_company
You can try it out yourself with this working repo: https://www.github.com/frank184/surveysays
And for reference:
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for

Rails custom order of records?

My Rails site has different categories that the user can browse through. Right now they're rendered in the view through a simple loop, but I'd like to be able to choose the order from left to right in which they're displayed.
Here's the current code:
<h4>Explore Listings</h4>
<% #categories.each do |f| %>
<p class="category-page-category"><%= link_to f.name, view_category_path(f.id) %></p>
<% end %>
What would be an easy solution to accomplish what I want to do?
The easiest way I know of how to do this is to set up an AJAX form which will pass an order parameter which will be handled by the controller to re-render the form. Your controller would then listen for the order parameter and append that to #categories.
For example, your controller code might look something like:
def index
#categories = Category.<some_other_scope>
#categories = #categories.order(order_param) if order_param.present?
respond_to do |format
format.js { render :index }
end
end
private
def order_param
params.permit(:order)
end
First of all native sorting by the DB is to be preferred in every case (it's much faster).
If your sorting does not depend on already existing Category attributes, migrate a new attribute like position:
add_column :categories, :position, :integer
and add a scope to the model:
class Category < ActiveRecord::Base
def self.ordered default_order=nil
return scoped if default_order.nil? or
not( %w(asc desc).include?(default_order.to_s) )
order("position #{default_order}")
end
end
and then in the controller:
def index
#categories = Category.ordered params[:order]
end
following the sorting attribute approach you also should ensure your position attribute is valid defined. So like:
class Category < ActiveRecord::Base
before_create :default_position
validates :position,
presence: true,
numericality: true,
uniqueness: true,
on: :update
def self.ordered default_order=nil
return scoped if default_order.nil? or
not( %w(asc desc).include?(default_order.to_s) )
order("position #{default_order}")
end
private
def default_position
self.position = self.class.maximum("position") + 1
end
end
Changing positions has to be implemented.
Otherwise if positions never change, this means Categories have to be sorted by their creation datetime. Then you also could sort by created_at instead of position.
It sounds like you want to sort the array of categories before they are shown, no?
If so you can use http://www.ruby-doc.org/core-2.1.2/Enumerable.html#method-i-sort_by or http://www.ruby-doc.org/core-2.1.2/Enumerable.html#method-i-sort to sort your categories however you want before rendering them.
So this could end up looking something like:
<% #categories.sort_by(&:name).each do |f| %>
assuming categories have a name field. This should probably be done in the controller or in the model with a scope (if you are sorting based on a database field) to keep this logic out of the view.
looks like acts_as_list is exactly what you need

Rails_Admin lookup in list

I have this model "Schedule" with attributes :id, :augurid, #and so on.
And I have another model "Augur" with attributes :id, :name, :email #and so on.
Now I want to display augur.name in the Schedule page in Rails_Admin, and my code is like following:
config.model "Schedule" do
list do
fields :id, :weekdays, :occupied, :starting, :duration
field :augur do
pretty_value do
augur = Augur.find(:augurid)
augur.name
end
end
end
edit do
fields :id, :augurid, :weekdays, :occupied, :starting, :duration
end
show do
end
end
But this code is not working, I don't know why since I'm new to rails, and I've checked the documentation of rails_admin, and looks like there's no solution to this problem.
Can anyone help me?
And I think the problem is around
augur = Augur.find(:augurid),
because if I change the :augurid to 5, it will display the right augur name. But how do I do that for every record in schedule?
You should just be able to do add object_label_method :name to the rails_admin configuration for the Augur model:
config.model "Augur" do
object_label_method :name
...
end
Is that what you're looking for?
As #JKen13579 mentioned, :object_label_method :name can do it for you. But if you really want to access the "current object" in your rails_admin callbacks, there is a way:
list do
field :augur do
pretty_value do
bindings[:object].name
end
end
end
This will show the name of the augur and only affect the list view.

Resources