rails scope multiple params - ruby-on-rails

I have a User model. My goal is to have a scope that returns users found by selected field .
Like
scope :starts_with, ->(filter, starts_with) { where("? like ?", "#{filter}", "#{starts_with}%")}
This is in my view:
<%= select_tag(:filter, options_for_select([["Name", "name"],
["E-mail", "email"]],
"name"), {:multiple => true}) %>
<%= text_field_tag :starts_with, params[:starts_with] %>
UPD I'm calling scope like that:
def index
#users = User.starts_with(params[:filter, :starts_with]).paginate(page: params[:page], per_page: 40)
end
At the moment it shows an error ArgumentError: wrong number of arguments(1 for 0)
Thanks for any help!

Replace your code in your action with:
def index
#users = User.starts_with(params[:filter], params[:starts_with]).paginate(page: params[:page], per_page: 40)
end
What's going on
In your original call you do User.starts_with(params[:filter, :starts_with]). Notice the params[:filter, :starts_with]. You are passing two arguments to the [] method of the params hash. This method only takes one argument. Check out the ruby doc for more info:
http://www.ruby-doc.org/core-2.1.2/Hash.html#method-i-5B-5D
params is a hash like any other hash you might want to use in your app or any ruby code. So, instead of doing params[:filter, :starts_with] that is not authorized, you need to ask for the value of the :filter key and :starts_with key separately and pass them as arguments to your scope like this:
User.starts_with(params[:filter], params[:starts_with])

Related

Scope with two parameters rails

I'm following this tutorial to implement a filtering feature in my Rails app. I want admins to be able to filter by age, identifier, and a date interval the users were created. It is the last bit that is causing me some headache.
In my model user.rb, I have defined the following scopes:
scope :created_between, -> (startdate, enddate) {where(created_at: startdate..enddate)}
scope :identified, -> { where.not(identifier: [nil, '']) }
scope :age, -> (age) { where("age > ?", age)}
In my controller users_controller.rb, I use a function to filter the params:
def search
filter = params.slice(:age, :created_between, :identified)
filter.each do |key, value|
if value.present?
#users = #users.public_send(key,value)
else
#users = #users.public_send(key)
end
end
end
I differentiate between a value present or not, since the :identified scope is implemented as a checkbox and therefore passes no value like
Lastly, I have created a form for all the possible filters like so, in my view.html.erb file:
<%= form_tag users_search_path, :method => :get, :enforce_utf8 => false do %>
<%= date_field :created_between, "from" %>
<%= date_field :created_between, "to" %>
<%= check_box_tag :identified, '', false %>
<%= text_field_tag :age, "age" %>
<% end %>
The filter for age and identified works. When I submit the form the query becomes /users/search?identified=&created_between[from]=&created_between[to]= when I only check the checkbox identified (the date_field is also passed although I did not submit any date). And /users/search?age=21&created_between[from]=&created_between[to]= when I only submit an age.
My problem is that whenever I try to submit two dates for the created_between scope I get an wrong number of arguments (1 given, expected 2) error. I'm not sure that I'm submitting the date fiels correctly.
How can I pass the two needed params to the scope? Or should I do it another way instead?
Sometimes just writing out the question seems to make things clearer. However, the solution was to alter my scope function for :created_between to:
scope :created_between, -> (date) {where(created_at: date[:from]..date[:to])}

update_all ruby on rails

Im trying to update all post where a condition is true. If the condition is true should the field category_id be set to params[:category_id]
Every time im trying to do it will my code update all post where the condition is true and set it to "--- !ruby/hash:ActionController::Parameters categori_id: '169'"
Instead of just 169.
My controller action look like this
def update_all_notes
#deletefolder = Categori.find(params[:id])
System.where(:categori_id => params[:id]).update_all(:categori_id => params[:categori_id])
redirect_to :back
end
My form look like this:
<%= form_tag update_all_notes_path(category.id) do %>
<%= collection_select :kategori_id, :id, #current_company.category.where.not(:name => category.name), :id, :name %>
<button>move</button>
this is the parameters i send to the action
"categori_id"=>{"categori_id"=>"169"},
"id"=>"168"}
Thanks in advance
From your hash you should replace params[:categori_id] to be params[:categori_id][:categori_id]
as the hash is { "categori_id" => {"categori_id" => 169}, "id" => X }
Example of update_all use:
ids = [1,2,3]
records = Mammal::Human.where(id: ids)
records.update_all(status: :enlightenment, enlightenment_at: Time.zone.now, enlightenment_by: "myself")
I will rather use status: value over :status => value syntax

global variable in application controller?

I grap the first country with
#country = Country.find(1)
Then i my head navigation i make this loop to get the right tags:
%ul.thumbnails
- #country.tags.find_each(:conditions => "active_house = true") do |a|
%li.span2
.thumbnail
- a.attachments.limit(1).each do |b|
= image_tag(b.file.url)
.caption
%p
#{link_to a.url, tag_country_region_houses_path(#country, a.name), :class => 'btn-nav-drop'}
This works fine. But the navigation is global so i created a method in application_controller like this:
helper_method :tags
def tags
#country = Country.find(1)
#tags = #country.tags.find_each(:conditions => "active_house = true")
end
And in the navigation view:
%ul.thumbnails
- tags do |a|
%li.span2
.thumbnail
- a.attachments.limit(1).each do |b|
= image_tag(b.file.url)
.caption
%p
#{link_to a.url, tag_country_houses_path(#country, a.name), :class => 'btn-nav-drop '}
But i get a error message "no block given (yield)"
Thanks..remco
Well, this is nothing about global variable which should be avoided whenever possible.
Your problem is in this line
tag_country_houses_path(#country, a.name)
There is NO #country exists in View.
You may wonder why. The reason is helper can't pass instance variables to View, different from controller.
What your helper all did is to return an array object #tags. The value of this object is available in view, but not instance variable #tags, neither #country.
The fix? Use something to replace #country. If the association is country has_many tags, you can do it like this:
tag_country_houses_path(a.country, a.name)
If not, you can set a method in tag model to get the country.
And you can even use some includes to make query more efficient but that is another story.
Also, your helper can be simplified without assigning any variables.
def tags
Country.find(1).find_each(:conditions => "active_house = true")
end
find_each accepts a block that will be yielded. Because you write find_each in helper without the block, so it will throw an error. You have two solutions.
Solution1: You can just use find to return an array.
def tags
Country.find(1).tags.find(:all, :conditions => "active_house = true")
end
in your view:
- tags.each do |t|
.........
Solution2: you can pass the block to your helper.
def tags
Country.find(1).tags.find_each(:conditions => "active_house = true") do |t|
yield t
end
end
in your view.
- tags do |t|
.........
If you have not many many records, just use solution1.

Ransack search not working if there is 'space' in search term

I am using ransack for search in my rails 3.2 application using postgres as database.
I have a Invoice model and every invoice belongs_to a buyer. Below is my search form in index page.
views/invoices/index.html.erb
<%= search_form_for #search do |f| %>
<%= f.text_field :buyer_name_cont %>
<%= f.submit "Search"%>
<% end %>
And here is my controller code.
controllers/invoices_controller.rb
def index
#search = Invoice.search(params[:q])
#invoices=#search.result(:distinct => true).paginate(:page => params[:page], :per_page => GlobalConstants::PER_PAGE )
respond_to do |format|
format.html # index.html.erb
format.json { render json: #invoices }
end
end
Let's say a invoice is there of a buyer having name "Bat Man".
If I search "Bat", I get the invoice in results.
Again if I search "Man", I get the invoice in results.
But if I search "Bat Man", I don't get the invoice in results.
I know it might be something trivial but I am not able to resolve.
Update
When I tried the sql query formed directly in database using pgAdmin, I realized that in database there were multiple spaces in the buyer name, something like "Bat.space.space.space.Man".
Can something be done so that "Bat.space.Man" search also finds "Bat.space.space.space.Man" in results?
You could sanitize your data. For instance with regexp_replace(). Run in the database once:
UPDATE invoice
SET buyer = regexp_replace(buyer, '\s\s+', ' ', 'g')
WHERE buyer <> regexp_replace(buyer, '\s\s+', ' ', 'g');
And sanitize new inserts & updates likewise.
\s .. class shorthand for "white space" (including tab or weird spaces).
The 4th parameter 'g' is for "globally", needed to replace all instances, not just the first.
Ransack not support cont search for multi terms, I solved the requirement my customized way. the details as following:
Add scope to your model:
scope :like_search, ->(column, value) {
keywords = value.to_s.split.map{ |k| "%#{k}%" }
where(Array.new(keywords.size, "#{column} ILIKE ?").join(' AND '), *keywords)
}
in your view. instead of using f.text_field :buyer_name_cont provided by ransack, use normal field helper text_field_tag :buyer_name, params[:buyer_name]
then restrict your ransack in scope:
scope = Invoice.like_search(:name , params[:buyer_name])
#q = scope.ransack(params[:q])

Using case statement in a helper

I haven't used case statements before and was wondering how to do the following.
I have a number of news pages, each having posts relevant to department, so the pages
/tynewyddnews
/woodsidenews
/outreachnews
/sandpipernews
On my home page I am grabbing the latest post from each of these and displaying them.
Post Model
def self.top_posts
#Array with each of the 4 departments - first record
top_posts = [
self.tynewydd_posts.first,
self.woodside_posts.first,
self.sandpiper_posts.first,
self.outreach_posts.first
]
#remove entry if nil
top_posts.delete_if {|x| x==nil}
return top_posts
end
tynewydd_posts for example is a scope
scope :tynewydd_posts, :include => :department, :conditions => {"departments.name" => "Ty Newydd"}, :order => "posts.published_on DESC"
So if i am reading a post on the home page from tynewydd and want to create a link to /tynewyddnews or i am reading a post from woodside and want to link_to /woodside I have been advised that a case statement may help, but I am unsure on what to use as parameters, so my attempt so far is
def public_news
case Post.top_posts
when :tynewydd_posts == 'Ty Newydd'
link_to('...Read more', tynewyddnews_path)
when :woodside_posts == 'Woodside'
link_to('...Read More', woodsidenews_path)
end
end
And then in my view i can call the helper
<%= public_news %>
obviously a miserable attempt, firstly in examples I have seen a variable is being set when the case is being set? if someone could give some advice on how to achieve this it would be much appreciated
Try this variant:
def public_news
path = case
when Post.tynewydd_posts then tynewyddnews_path
when Post.woodside_posts then woodsidenews_path
...
else default_path end
end
link_to('...Read More', path)
end
when expression will fire up when expression is evaluated to true

Resources