Ransack eq_any for boolean values - ruby-on-rails

I have a model that has a boolean field called ignored. What I did right up to this point is not display records which have this field set to true by having a default_scope where(:ignored => false).
Now, in my Ransack search form I would like to have a checkbox named Include ignored so the table would also display the records which are ignored along with the ones which aren't.
I got as far as adding the checkbox to my search form f.check_box(:ignored_eq) but when checking this, the list will display ONLY the ignored records. How do I display records in all states when checking this box?
Controller:
def index
#q = Venue.search(params[:q])
#venues = #q.result.page(params[:page]).per_page(15)
end
Search form (HAML):
= search_form_for #q, url: [:admin, :venues] do |f|
= f.text_field :name_cont
= f.text_field :locality_name_cont
= f.text_field :categories_name_cont
= f.check_box :ignored_eq
= content_tag :button, type: :submit, class: 'btn' do
= content_tag(:i, "", class: "icon-search")
= t("helpers.links.search")
The desired functionality is: when I enter the page I want to have only the ones that have ignore = false listed and have the box unchecked. When I check the box and submit the form I want to have ignored as well as non-ignored ones displayed. Is this possible with Ransack?

What you can do is use the rewhere AR method.
and have something like that for the ignored flag:
def index
#q = Venue.search(params[:q])
#q = #q.rewhere("ignored = 0 OR ignored = 1") if params[:ignored_eq] == 1
#venues = #q.result.page(params[:page]).per_page(15)
end
you can also use the unscoped method to clear the default_scope
def index
if params[:ignored_eq] == 1
#q = Venue.unscoped.search(params[:q])
else
#q = Venue.search(params[:q])
end
#venues = #q.result.page(params[:page]).per_page(15)
end
which is prettier (IMO)

Related

Rails - filter by date range with 2 models

I have two models: space and booking. Space has_many bookings and Booking has two date attributes: check_in and check_out.
Given a valid date range, I want to show all spaces available during this range
This is the view:
<%= form_tag spaces_path, method: :get do %>
<%= date_field_tag :query1,
params[:query1],
class: "form-control" %>
<%= date_field_tag :query2,
params[:query2],
class: "form-control" %>
<%= submit_tag "Search", class: "btn" %>
<% end %>
This is the SpaceController:
(...)
def index
if params[:query1].present? && params[:query2].present?
query1 = DateTime.parse(params[:query1])
query2 = DateTime.parse(params[:query2])
search = query1..query2
bookings = Booking.all
# returns the bookings that overlaps with the search
overlapping_bookings = bookings.select do |booking|
check_in = booking[:check_in]
check_out = booking[:check_out]
period = check_in..check_out
search.overlaps?(booking.period)
end
# returns the spaces_id of the bookings that overlaps
overlapping_space_ids = overlapping_bookings.select do |overlapping_booking|
overlapping_booking[:space_id]
end
# remove the duplicates
overlapping_space_ids.uniq!
# remove the spaces with bookings that overlap with the search
#spaces = Space.all.reject do |space|
overlapping_space_ids.include? space[:id]
end
else
#spaces = Space.all
end
end
(...)
I assume the root cause of my problem is that I'm treating the Active Record Query Object as an array of hashes, not sure if it's correct. I made some researches on this but I haven't found any exhaustive answer.
Using an SQL subquery (in PostgreSQL for example) you would do this:
sql = <<SQL
SELECT *
FROM spaces
WHERE id in (
SELECT space_id
FROM bookings
WHERE
(check_in, check_out) OVERLAPS (:from, :to)
)
SQL;
Booking.find_by_sql([sql, {from: query1, to: query2})
Hope that helps :)
I would add a scope to the Booking model first:
# in app/models/booking.rb
scope :overlapping, ->(from, to) {
where(
"(check_in, check_out) OVERLAPS (?, ?)", from, to
)
}
and would then change the whole controller method to:
def index
#spaces = Space.all
if params[:query1].present? && params[:query2].present?
from = DateTime.parse(params[:query1])
to = DateTime.parse(params[:query2])
#space = #space.where.not(
id: Booking.select(:space_id).overlapping(from, to)
)
end
end

Select2 rails form repopulation

I created a rails (v5) form with multiple select and collection_select elements.
Then I use Select2-rails (v4.0.3) to allow a nice selection looking like tags.
The search-options are pulled by ajax.
It works fine until one presses the submit-button with missing required fields.
Valid field-content has now been deleted from the field.
Let me give some example-code:
controller:
...
def form
if params[:form_request].nil?
#form_request = FormRequest.new
else
#form_request = FormRequest.new(params[:form_request])
end
end
def request_form
#form_request = FormRequest.new(params[:form_request])
if #form_request.valid?
render :summary
else
render :form
end
end
...
form:
...
<%= bootstrap_form_for(#form_request, url: '/form/request_form') do |f| %>
<%= f.select :field, [], {label: 'Field label'} %>
<%= f.submit "Submit form" %>
<% end %>
:field is for sure a writable field in the model (and data is set fine)
coffee-script:
Query ->
$("#form_request_from").select2({
ajax: {
url: func =(params) ->
filter = params.term
return "/data.json?filter=" + filter;
,
dataType: 'json',
processResults: processData
},
theme: 'bootstrap',
placeholder: 'Enter data here'
});
processData = (data) ->
mapdata = $.map(data, func =(obj) ->
obj.id = obj.id;
obj.text = obj.name;
return obj;
);
return { results: mapdata };
I am thinking of a lot of possibilities, but at the end I am not sure where the field-data comes from. It is inside the object, but it isn't written to the resulting HTML in any way.
And even if the id would be written as a selected option,
the select2 script would need to know how to transform that into the string to show the real data.
Any idea how to achieve that the data is still written into a field after a failing validation?
After trying out some things I found out how to do it.
At first I just changed the empty array to be the :field variable,too.
This doesn't work too well because it only remembers the ID of the value that has been entered before and like this the SELECT2-script could not find the value to that key and nothing is shown.
Then I created a new variable inside the controller in which I place the array with name and id:
field_object = ObjectsModel.find(#form_request.field.to_i)
#form_field = []
if !field_object.nil?
#form_field = [[field_object.name, field_object.id]]
end
And in the view I now use this field to show the available options:
<%= bootstrap_form_for(#form_request, url: '/form/request_form') do |f| %>
<%= f.select :field, #form_field, {label: 'Field label'} %>
<%= f.submit "Submit form" %>
<% end %>
This works perfectly fine for me without the need to touch the SELECT2-script.
The possible values are still fetched by AJAX but already filled out fields will persist upon redirect to another action.

Passing params without nil

I want to pass the value of a form to a Controller that should call a method from the Model for searching products.
I want to pass the category_id and the string that the user writes at the textfield.
I would pass to the model even the blank value of params (if the user doesn't write something on the search bar or choose none category)
I want to save even the blank value cause in the model, the SQL blank variable take "all" things.
And this is good a cause if there is something in the params , the SQL will find that thing, BUT if there is nothing in the params, the SQL will take all products.
I'm not able to save the value of params in varibiles cause if params is blank it returns to me this error:
undefined method `[]' for nil:NilClass
I hope you understand me and what I want to do.
In other words , I want to use a simple assignment to pass a value (even blank values) to a model to do a SQL query. In one shoot of code I want to program two cases.
Here my code.
In my Controller:
...
if params[:search]
#search_name = params[:search]
end
if params[:category][:name]
#category = params[:category][:name]
end
#products = Product.search(#search_name,#category)
...
In my Model:
def self.search(search_name,category)
ricerca = Product.where("category_id = ? ",category)
ricerca = ricerca.where("title like ? ", "%#{search_name}%")
end
You can get category parameters by defining a method like,
def category_params
params.fetch(:category, {})
end
And, then look up for Products by,
#search_name = params[:search]
#category = category_params[:name]
#products = Product.search(#search_name, #category)
In my opinion, if you are making a direct query on Product, then you should do like,
def product_params
params.fetch(:product, {})
end
#category = product_params[:category_id]
#title = product_params[:title]
#products = Product.search(#category, #title)
And in product model,
def self.search(category, title)
where("category_id = ? AND title LIKE ?", category, "%#{title}%")
end
Try this:
#search_name = params[:search]
#category = (params[:category].blank? ? nil : params[:category][:name])
#products = Product.search(#search_name,#category)
#Vito, change the line -> #category = params[:category][:name] in your controller by
#category = params[:category].present? ? params[:category][:name]
the issue is when you don't select the category then params[:category] comes nil and you are fetching name from params[:category] that's why error "undefined method `[]' for nil:NilClass" is comming.
Hope this will resolve your problem.
i still have problems , cause when i wrote
if params[:category]
#category = params[:category][:name]
else
#category = " "
end
if params[:search]
#nome_ricerca = params[:search]
else
#nome_ricerca = " "
end
#products = Product.search(#nome_ricerca,#category)
i have this problem:
Showing C:/Bitnami/depot/app/views/store/index.html.erb where line #18 raised:
undefined method `name' for "":String
Extracted source (around line #18):
<p>
<%= label_tag :search, 'Search:' %>
<%= text_field_tag :search, params[:search] %>
<%= collection_select :category, :name, Category.all, :id, :name, {:prompt => 'All Categories'} %>
<%= submit_tag "Search", name: nil %>
</p>
what is this?
Ok, i fixed the problem
it was (i think) concerning the fact that i used #category as variable name
maybe the view gets in confusion for this
i call #category in #cat and the problem disappears
But , now the problem is that the SQL query doesn't return all the category if i pass "" in the variable #cat
How can i have all the result of a query? with like %% it works but with
category_id = "" no. why?
I post the code fixed for others:
In Controller:
if params[:category]
#cat = params[:category][:name]
else
#cat = ''
end
if params[:search]
#nome_ricerca = params[:search]
else
#nome_ricerca = ''
end
#products = Product.search(#nome_ricerca,#cat)
In model:
def self.search(nome_ricerca,categoria)
ricerca = Product.where("title like ? AND category_id = ? ", "%#{nome_ricerca}%",categoria)
end
LAST POST:
PROBLEM FIXED:
In Controller
#stringa_sql = ""
if params[:search]
#nome_ricerca = params[:search]
else
#nome_ricerca = ''
end
#stringa_sql = "title like "+"'%"+#nome_ricerca+"%'"
if params[:category]
if params[:category][:name] != ""
#cat = params[:category][:name]
#stringa_sql += " AND category_id = "+#cat
else
#cat = ''
end
end
#products = Product.search(#stringa_sql)
In Model:
def self.search(stringa_sql)
ricerca = Product.where(stringa_sql)
end
Question: does this solution suffer Sql Injection ?
Thx all :)
I Hope this solution will help someone.

Paginate with kaminari + query string sorting leads to strange links

In Brief
When using query string parameters to filter a list of objects, the links in the Kaminari pagination helper are wrong.
Background
I've got an action LearningObjects#index that supports basic sorting that I implemented my self and pagination via Kaminari.
Sorting is accomplished using a normal form that performs GET requests.
The controller:
class LearningObjectsController < AdministrationController
respond_to :json, :html
load_and_authorize_resource
# GET /learning_objects
# GET /learning_objects.json
def index
# Searching
if LearningObject.has_search_params(params)
#learning_objects = LearningObject.search(params)
end
# Pagination
#learning_objects = #learning_objects.page(params[:page]).per(params[:per])
respond_with #learning_objects, serializer: LearningObjectPageSerializer, total_records: #learning_objects.total_count
end
# other actions...#
end
The view
%h1= t("actions.learning_object.index")
.well
%h4= t("actions.filter")
= form_tag(learning_objects_path, method: :get, class: "form-search") do
= select_tag :subject_id,
options_from_collection_for_select(#subjects, "id", "name", params[:subject_id].to_i),
prompt: t("activerecord.models.subject")
= select_tag :stage_id,
options_from_collection_for_select(#stages, "id", "name", params[:stage_id].to_i),
prompt: t("activerecord.models.stage")
= text_field_tag :free_text, params[:free_text], placeholder: t("actions.write_something")
= submit_tag t("actions.filter"), class: :btn
= link_to t("actions.clear"), learning_objects_path, class: :btn
%p= t("actions.learning_object.show_number", count: #learning_objects.total_count, total: #total)
= paginate #learning_objects
= table_for #learning_objects do |t|
= t.data actions: :all do
=t.cell(:name) {|l| link_to l, l}
=t.cell(:downloads) {|l| "#{l.downloads} #{t("misc.times", count: l.downloads)}"}
=t.cell(:language)
=t.cell(:active) {|l| tag("i", class: l.active ? "icon-ok" : "") }
= link_to t("actions.learning_object.new"), new_learning_object_path
The problem
When the form is used to filter the LearningObjects, the pagination links generated by Kaminari go all strange.
For example, if the address looks like this: http://localhost:3000/learning_objects?utf8=%E2%9C%93&subject_id=&stage_id=&free_text=obj&commit=Filtrera
Then the pagination link looks like this: http://localhost:3000/subjects//learning_objects?commit=Filtrera&free_text=obj&page=2&stage_id=&utf8=%E2%9C%93
I would expect the pagination link to look like this: http://localhost:3000/learning_objects?utf8=%E2%9C%93&subject_id=&stage_id=&free_text=obj&commit=Filtrera&page=2
The question
Why does Kaminari generate these malformed nested links when the search terms are present in the query string?

Rails problem display attribute key along with attributes value

I have the following problem. I have a form which takes input for a "Chart" object. But after processing the form, i wish to display one of the values, and it adds the key of this value.
Class model
class Chart
attr_accessor :title, :series
def initialize(title = nil, series = [])
#title, #series = title, series
end
end
View of form:
<% form_for :chart , :url => { :action => "show" } do |f| %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>...
<% end %>
Chart controller, show method:
def show
#chart = Chart.new(params[:chart])
end
View of show:
<h2><%=h #chart.title %></h2>
Which displays: "title"input_forms_title""
for example: writing in the input form: Economy, prints in the show view: "titleEconomy"
Any ideas?
I have just figured it out. The problem was in the constructor or initialize method. By changing the initialize method to:
def initialize( options = {} )
#title = options[:title]
#series = []
end
It now accepts all params perfectly!

Resources