Rails 4: ActiveAdmin clears out an array field upon update - ruby-on-rails-4

I have an ActiveAdmin model registered as such:
ActiveAdmin.register MyModel do
permit_params :name, :synonyms
filter :name
index do
selectable_column
column :name
actions
end
end
I noticed that when I update an object from the ActiveAdmin interface, the synonyms are getting cleared out. Synonyms are an array field defined as such in the PostgreSQL database:
synonyms text[] DEFAULT '{}'::text[]
I tried putting the following in app/admin/my_model.rb:
controller do
def update
permitted_params[:my_model][:synonyms] = JSON.parse permitted_params[:my_model][:synonyms]
super
end
end
and I also tried it with params instead of permitted_params but that doesn't work either. Not sure why ActiveAdmin would be discarding this field. Am I doing something incorrectly? The project I'm working with uses ActiveAdmin 1.0.0.pre4 (I realize this is a dated version).
Any advice would be much appreciated. Thanks in advance!
Notes: Seems this might be a common issue?

I'm not sure if this is applicable to string arrays and I don't know about that specific version of Activeadmin, but I encountered this issue in a slightly different context.
I had a model where the array data types were decimals & integers. I had to explicitly state the type of form input to be used when editing / updating the record or nothing was passed back from the field with the update parameters. Activeadmin chose a number input by default, but it needed to be processed as a string.
form do |f|
f.inputs do
f.input :ages, as :string, :input_html => {:maxlength => '100'}
end
end
I had to set maxlength manually because for some reason it was automatically being set very short. In the model, the string then has to be converted into an actual array before being saved.
def ages= items
if items.is_a? String
items = items.split(' ')
items.each do |i|
i.to_i
end
end
super items
end

Related

rails unchanged file fields got deleted on updating nested forms with carrierwave

I've two models: ad and variant
Model: Ad
has_one :variant
accepts_nested_attributes_for :variant
Controller AdsController strong parameters:
params.require(:ad).permit(:title, :desc, variant_attributes: [:custom_image_1, :custom_image_2, :custom_image_3])
View ads/_form.slim
= f.fields_for :variant, #ad.variant || Variant.new do |va|
- 3.times do |i|
= va.file_field "custom_image_#{i+1}"
In view I added the nested form fields using a loop. The problem is when I create any AD, that time suppose I upload only one image in variant form (custom_image_1). Now, I came back for editing and uploaded another image on the variant (custom_image_2).
After the update, I saw that my previously uploaded custom_image_1 is deleted and only custom_image_2 is present in the database.
What is the reason behind it?
I check the params while submitting the edit form. There only contains the custom_image_2 in submitted attributes.
Hope someone will find that useful:
In my strong parameters, I need to include :id to resolve this issue.
params.require(:ad).permit(:title, :desc, variant_attributes: [:id, :custom_image_1, :custom_image_2, :custom_image_3])

Assign nested attributes records to current user when using Cocoon gem

In my application I have models Post & Slides & I have:
class Post < ActiveRecord::Base
has_many :slides, inverse_of: :post
accepts_nested_attributes_for :slides, reject_if: :all_blank, allow_destroy: true
Everything works fine, only thing I need (because of how my application will work), is when a slide is created, I need to assign it to current_user or user that is creating the record.
I already have user_id in my slides table and:
class User < ActiveRecord::Base
has_many :posts
has_many :slide
end
class Slide < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
My PostsController looks like this:
def new
#post = current_user.posts.build
// This is for adding a slide without user needing to click on link_to_add_association when they enter new page/action
#post.slides.build
end
def create
#post = current_user.posts.build(post_params)
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Was successfully created.' }
else
format.html { render :new }
end
end
end
Any help is appreciated!
There are two ways to accomplish this:
First option: when saving the slide, fill in the user-id, but this will get pretty messy quickly. You either do it in the model in a before_save, but how do you know the current-user-id? Or do it in the controller and change the user-id if not set before saving/after saving.
There is, however, an easier option :) Using the :wrap_object option of the link_to_add_association (see doc) you can prefill the user_id in the form! So something like:
= link_to_add_association ('add slide', #form_obj, :slides,
wrap_object: Proc.new {|slide| slide.user_id = current_user.id; slide })
To be completely correct, you would also have to change your new method as follows
#post.slides.build(user_id: current_user.id)
Then of course, we have to add the user_id to the form, as a hidden field, so it is sent back to the controller, and do not forget to fix your strong parameters clause to allow setting the user_id as well :)
When I'm looking at this I see three ways to go about it, but since you're on cocoon already, I would drop the connection between user & slides - as it kind of violates good database practices (until you hit a point where you page is so popular you have to optimize of course, but that would be done differently).
You are using cocoon, but you're not utilizing the nesting of the relationship fully yet ...
The best practice would be to have cocoon's nesting create both & instead of trying to assign to current_user you call something like:
#slides = current_user.posts.find_first(param[:id]).slides
The #slides saves all the results, the .Post.find(param[:id]) finds a specific post for current_user.
Note: this is not the most optimized way & I haven't tested this, but it shows you the format of one way you can think about the relationships. You will need to hit rails console and run some tests like ...
(rails console)> #user = User.first
Next we test that there are posts available, as it's frustrating to test blanks & not get the results ...
(rails console)> #posts = #user.posts
Then we use the find method & I'm going to use Post.first just to get a working id, you can easily put "1" or any number you know is valid ...
(rails console)> #post = #posts.find(Post.first)
Finally, we go with either all slides to make sure its a valid dataset
(rails console)> #post.slides
If you want a specific slide later & have a has_many relationship just tag that find method on the .slides after.
Also one last thing - when you state earlier in there you need the current_user to be related, you can use an entry in your model.rb to create a method or a scope to get the data & allow you to link it to the current_user more easily & even drop some directed SQL query with the .where method to pull that information up if performance is an issue.
I spotted a second optimization in there ... if everything really is working - don't worry about this!
And don't forget about the strong_parameters nesting to do this fully ... Strong Param white listing
Basic format ... `.permit(:id, :something, slide_attributes: [:id, :name, :whatever, :_destroy])

Ajax Datatables Rails - Query not working for show-method

Let's start with the database-model:
# => ProductSelection.rb
has_many :products, -> { uniq }, through: :product_variants
# => Product.rb
has_many :product_informations
belongs_to :product_configuration
belongs_to :product_class
Using plain Ruby on Rails, we collected the products to show inside the product_selection#show-method like so:
#products = ProductSelection.find(params[:id]).products.includes(:product_informations, :product_class, :product_configuration)
and generated a table like so:
= table(#products).as(:products).default do |product|
= product.name
= product.product_configuration.name
= product.product_class.name
= product.state
= link_to product_product_selection_path(#product_selection, product_id: product.id), method: :delete
Now we want to use Datatables instead of plain Ruby on Rails.
We are using the Ajax-Datatables-Rails-Gem. We would like all columns to be sortable and searchable in the end.
Unfortunately we do not come past the query to retrieve the Products belonging to the ProductSelection in question.
This is the query we tried so far:
def get_raw_records
ProductSelection.find(params[:id]).products.includes(:product_informations, :product_class, :product_configuration).references(:product_information, :product_class, :product_configuration).distinct
end
def data
records.map do |record|
[
record.name,
record.product_configuration.name,
record.product_class.name,
record.state,
record.id
]
end
end
The error that pops up:
> undefined method `each_with_index' for nil:NilClass
When adding in sortable/searchable columns, the error is the following instead:
PG::UndefinedColumn at /product_selections/fetch_table_data_show.json
=====================================================================
> ERROR: column products.name does not exist
LINE 1: SELECT DISTINCT "products"."id", products.name
with the configuration like so:
def sortable_columns
# Declare strings in this format: ModelName.column_name
#sortable_columns ||= %w(Product.name ProductConfiguration.name ProductClass.name Product.state)
end
def searchable_columns
# Declare strings in this format: ModelName.column_name
#searchable_columns ||= %w(Product.name ProductConfiguration.name ProductClass.name Product.state)
end
I think the problem is with the different models right here. I assume Datatables-Rails expects a model of ProductSelection but instead is prompted with a Product. Any help would be highly appreciated!
If anything is missing, let me know!
After having another look at this, we figured the problem was that Product.name wasn't a valid ActiveRecord-construct. Instead it was a helper method defined somewhere down the road.
Since the gem tries to find records through the column-names, this error occurred.
We solved it by removing the column in question. Other approaches would be to:
Store the data in a separate column, instead of using a helper.
Implementing the helpers behavior in javascript so that we can pass
it as a callback to datatables.

Globalize: How to show in the view the current locale information

I am currently having a bit of a problem with Globalize gem.
I explain the current situation:
I have a Model called Question. After creating it, without any data stored, I added the following lines to the model:
class Question < ActiveRecord::Base
translates :wording, :answer1, :answer2, :answer3, :answer4
end
Then, I created a migration to create the translations table
class CreateTranslationsTable < ActiveRecord::Migration
def up
Question.create_translation_table! :wording => :string, :answer1 => :string, :answer2 => :string, :answer3 => :string, :answer4 => :string
def end
def down
Question.drop_translation_table!
def end
My default locale is :en. After that I added some data.
If I go execute rails c and put the command Question.first.wording everything works fine.
Although when I execute in 'rails c' I18n.locale = :es and then I do Question.first.wording still displays the english text I put at the beginning.
I tried one thing which it seemed to help me is that I dropped all the translated columns (like specified in Globalize documentation after you migrated data. In my case I didn't have any data to migrate at the beginning though). After that I made a rollback (which got back the columns I deleted form the Question model), then executing Question.first.wording with I18n.locale = :es got it working. Which means that Question.first.wording returns nil.
After that, I implemented the 'Locale from Url Params' as specified in the Ruby on Rails guide
Which means the first URL param si the ':locale' param.
Now the current problem: The view still displays the information in English when it should display it in Spanish, since the URL I entered was http://localhost.com/es/questions/.
How can I make it to display in the view the Spanish information?
My mistake. I interpreted from the documentation that the the chunck of code (in application_controller.rb) that works for setting the url:
def default_url_options(options={})
{ locale: params[:locale] }
end
would actually set the 'I18n.locale' variable. What I did is the next to get around this (in application_controller.rb):
before_action :change_to_current_locale
def change_to_current_locale
I18n.locale = params[:locale]
end
That made it work.

Rails 4 - Column exists but does not update

I have recently added a field "tag" to my blog app built in Rails 4. Below you can see the field appearing in the Edit view:
But once I return to the Show view after editing, this does not appear:
When I check the database directly I can definitely see it exists:
sqlite> PRAGMA table_info(POSTS);
0|id|INTEGER|1||1
1|title|varchar(255)|0||0
2|body|text|0||0
3|created_at|datetime|0||0
4|updated_at|datetime|0||0
5|slug|varchar(255)|0||0
6|tag|varchar(255)|0||0
Can anyone suggest what is going on or how to troubleshoot this?
Rails 4 uses strong parameters by default. This means you have to explicitly whitelist params you wish to mass assign.
When adding a new attribute to a model, you have to remember to update the permitted params in you controller.
For example, in your case, you would need to make sure :tags are added like so:
class PostController < ActionController::Base
def update
post = Post.find(params[:id])
post.update(post_params)
redirect_to post
end
private
def post_params
params.require(:post).permit(:title, :body, :tag)
end
end