Ajax Datatables Rails - Query not working for show-method - ruby-on-rails-4

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.

Related

Rails 4: ActiveAdmin clears out an array field upon update

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

How does ransack search works on an array list?

I am using Rails 4.2.5, Ruby 2.2, ransack.
I am trying to implement search functionality using Ransack. I have something like:
emails = ["abc#abc.com", "a#a.com", "b#b.com"]
users_list = emails.map{|a| User.where(email: a).first}
checked_in_users = Kaminari.paginate_array(users_list)
This gives me proper list of users in the page. But if I want to search by email, what should I do ?
#q = checked_in_users.ransack params[:q]
This gives me:
"NoMethodError (undefined method `ransack' for #<Array"
HAML code:
= search_form_for [#q], url: users_path(some_id: id) do |form|
= form.text_field :user_name_or_user_email_cont, placeholder: 'Name or email', class: 'form-control'
What would be the correct way to do it with ransack ?
If you want to search the entire list of users, you could do something like this:
#q = User.ransack(params[:q])
#users = #q.result.page(params[:page])
You want to call ransack on your model prior to adding pagination. Once you have the ransack result, you can add pagination. This section of the documentation shows you how to handle pagination with ransack. In its most basic form, this is how you can use ransack.
Also, it looks like you have some odd code in your example. Apologies if I'm misunderstanding what you're trying to do.
When you call emails.map{|a| User.where(email: a).first}, you're making a where query 3 times in this case and returning an array of 3 models. Ransack won't operate on that array.
I would change it to something like this in your case:
emails = ["abc#abc.com", "a#a.com", "b#b.com"]
#q = User.where(email: emails).ransack(params[:q])
#checked_in_users = #q.result.page(params[:page])
Just know that you'd be searching from an array of users with emails of "abc#abc.com", "a#a.com", and "b#b.com". If you search with your ransack form for "bob#example.com", you wouldn't get any search results. This may not be what you want.

Ruby on Rails - Cocoon gem, Don't Pre-populate existing/added posts in edit action

My application has models Campaign & Post, I have:
class Campaign < ActiveRecord::Base
has_many :posts, inverse_of: :campaign
accepts_nested_attributes_for :posts, reject_if: :all_blank, allow_destroy: true
class Post < ActiveRecord::Base
belongs_to :campaign
My form:
= simple_form_for(#campaign) do |f|
= f.error_notification
= f.input :title
#posts
= f.simple_fields_for :posts do |post|
= render 'post_fields', f: post
.links
= link_to_add_association 'Add Post', f, :posts, wrap_object: Proc.new {|post| post.user_id = current_user.id; post }
I use Cocoon gem for nested_forms.
When I go to my campaigns#edit view, I can see all posts that were already added to a campaign (natural behavior of the gem), and I can add new posts to my campaign and/or edit existing posts .
I have also a button that has this param: add_to: 'existing_campaign' and what I am trying to achieve is, if my link has ?add_to=existing_campaign, I don't want to show/Pre-populate any of the posts that were already added to campaign, so user can only add new posts to the campaign
My link_to looks like:
= link_to 'Add Post', edit_campaign_path(campaign, add_to: 'existing_campaign'),
short explain: if edit link has param ?add_to=existing_campaign, I don't Pre-populate already added posts, if param doesn't exists, I Pre-populate posts
How can I achieve this?
Set up an attr_accessor in campaign to control whether or not existing posts should be seen...
class Campaign < ActiveRecord::Base
attr_accessor hide_posts
...
end
Set the value in your edit method
class CampaignsController < ApplicationController
def edit
#campaign.hide_posts = params[:add_to] == 'existing_campaign'
...
end
Ensure the temporary variable is in your strong parameters (so that redisplay after failed update remembers to hide posts)
def campaign_params
params.require(:campaign).permit( :hide_posts, ...
Now on your view you can do...
= f.hidden_field :hide_posts
= f.simple_fields_for :posts do |post|
= render('post_fields', f: post) unless #campaign.hide_posts && post.object.persisted?
Updating with more specifics ...
You as the design need to decide if you want to move the logic to a helper. If you aren't aware of skinny controller / fat model or another strategy for organizing your codebase read this ... Link. For now, I'm going to assume you will use a helper.
The theory & some nitpicky things ...
What's going to happen with skinny controller is you need that parameter accessible in the view logic (but that would be insecure and violate the rails way). Since you shouldn't just expose params to the view, you instead are passing the message containing the parameter's value. Object orientation teaches us to use pass messages. Rails says to use instances variables (#something) in the controller and the view has access to those.
We need to setup the conditional logic next. One of the Rails ways is to use helpers to remove extraneous logic from the view or make it readable. This qualifies as something that unless you have another reason I'm not aware of - this should be in a helper. I would be tempted here to just test for boolean and call another partial for the extra view you make.
Which means I have to assume your tests will change too (if not done in a standard way, you have to include to get access to that method/object).
Specific to your question
link_toon the page calling the nested form should be true/false...
= link_to 'Add Post', edit_campaign_path(campaign, show_posts: false)
Your controller will have #show_posts in whatever action of the campaign controller you are using (edit or new usually). You need to set #show_posts = params[:show_posts]
Write the helper ...
helpers/campaign_helper.rb
def showPosts?(show_posts)
testPart == true ? 'only_comment' : 'post_fields'
end
A new partial ... which is basically the same, but drops the simple_fields_for loop which populates the other posts.
Your _form partial will change from what you had above to ...
= f.simple_fields_for :posts do |post|
= render 'posts_fields', f: post
To ...
...
#posts
= render showPosts?(#show_posts), f: post
...
Update, I tested all the parts & got it working with your exact syntax - I ended up using ternary operator in the helper.

Rails 4: strong_params,nested_attributes_for and belongs_to association trouble

I really can't get my head around Rails 4 strong parameters, belongs_to association and form with fields_for.
Imagine I have model for quoting some price:
class Quote < ActiveRecord::Base
belongs_to :fee
accepts_nested_attributes_for :fee
Now, I have seeded some fees into the db, and have put some radiobuttons on my form_for #quote using fields_for. The values of the radiobuttons are simply ids of the records.
Here is the troubling part, the controller:
def create
#quote = Quote.new(quote_params)
...
end
def quote_params
params.require(:quote).permit(:amount_from, fee_attributes: [:id])
end
From my understanding, automagically Rails should fetch fee record with some id, but there is some mystic error instead.
params hash is: "quote"=>{"amount_from"=>"1200", "fee_attributes"=>{"id"=>"1"}}
Log tail:
Completed 404 Not Found in 264ms
ActiveRecord::RecordNotFound (Couldn't find Fee with ID=1 for Quote with ID=)
app/controllers/quotes_controller.rb:14:in `create'
I really don't understand what is going on here, have read Rails association guide, googled for hour for all info, but to no avail.
What I want to achieve here is to understand the correct "Rails way" to fetch some associations for new Quote object using some params I've put in the form.
Guess I got nested_attributes_for wrong, somehow thought it would call Fee.find automagically.
I've opted for ditching fields_for helpers from the form and rendering fields manually like
radio_button_tag 'fee[id]', fee.id
Then in controller I have 2 params methods now:
def quote_params
params.require(:quote).permit(:amount_from)
end
def fee_params
params.require(:fee).permit(:id)
end
And my action looks like
def create
#quote = Quote.new(quote_params)
#quote.fee = Fee.find(fee_params[:id])
...
Any additions on best practices when one has to handle lots of different objects with not so straight init logic are welcome.

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.