Pundit Policy Scope with Ransack query - ruby-on-rails

I am trying to use Ransack to filter records that have an applicable policy_scope from Pundit.
class ItemsController < ApplicationController
def index
#q = policy_scope(Item).ransack(params[:q])
#items = #q.result(distinct: true)
end
end
This returns an error:
ActionView::Template::Error (PG::UndefinedFunction: ERROR: could not identify an equality operator for type json
LINE 1: SELECT DISTINCT "items".* FROM "items" WHERE "items"."...
I know I'm not using Pundit and Ransack right in this instance, but I'm at a loss as to how I filter a set of scoped records.
What magic should I be doing?

Related

Sorting a result defined by remote parameters

i have this index action:
def index
limit = params[:limit]
page = params[:page]
sort = params[:sort].split(',')
#term = nil
if params[:search]
#term = params[:search]
#lessons = policy_scope(Lesson).search(#term)
.order("#{sort[0]} #{sort[1].upcase}")
.paginate(page: page, per_page: limit)
else
#lessons = policy_scope(Lesson).order("#{sort[0]} #{sort[1].upcase}")
.paginate(page: page, per_page: limit)
end
end
which is fed by a vuejs frontend with a vuetify datatable and its purpose is to send out an array of lesson objects, filtered and sorted by the frontend.
this works pretty good with default rails..
however, with the mobility gem involved there is no fieldname "title" for example or "title_en", so the order stops working. in mobility you have some "fake column names" and it automagically handles it to search for the value in a key-value table ( https://github.com/shioyama/mobility#getting-started )
so i sat me down and fired up the console and figured out that:
.order(title: :desc) - works
.order(title_en: :asc) - works
everything with strings involved, like .order('title DESC') or .order('title_en ASC') does not work and results in an error like
ActionView::Template::Error (PG::UndefinedColumn: ERROR: column
"title_en" does not exist LINE 1: SELECT "lessons".* FROM "lessons"
ORDER BY title_en ASC LIMI...
is there a way i could get this working? maybe there is something to generate title: out of 'title'? Or some other magic?
thanks!
You can call the order method like this instead:
.order(sort[0] => sort[1])
Passing "a" => "b" to a method is the same as passing "a": "b".

Sorting 2 colums in a table in Ruby on Rails using haml

I am kind of new at rails, currently using version 3.23 and I am trying to enable 'sort' on two columns in my table. I managed to create the links on these column headers and actually got one column working/sorting!But couldnt achieve the same result when I modified my code in the movie controller rb, my code can only work for one column! is:
def index
#movies = Movie.all.sort_by { |movie| movie.title }
end
Works perfectly, but when I combine another parameter i.e. release date I get an error!
def index
#movies = Movie.all.sort_by { |movie| movie.title } then { |release date| release.date}
end
Can someone please help me resolve this issue? I have researched it on google but I've gotten nothing conclusive!. Any help will be most appreciated.
Assuming that you have to sort on title and release_date fields in movies table.
You can perform the sorting at database level itself as below:
In Rails 4.x:
Below will sort all the movie records with title and release_date in ascending order(as default).
def index
#movies = Movie.order(:title, :release_date)
end
If you want to change the order, you can specify as asc or desc as:
def index
#movies = Movie.order(title: :asc, release_date: :desc)
end
In Rails 3.x:
def index
#movies = Movie.all(:order => "title ASC, release_date ASC")
end
If you want to change the order, you can specify as DESC in the above case.
You can order your listing by this
def index
#movies = Movie.order(title: :asc, release_data: :desc)
end

Meta_search error with model associations

I have these in my controller:
class AccountsController < ApplicationController
def index
#search = Account.search(params[:search])
#accounts = #search.order("id desc").includes(:chef).page(params[:pagina]).per(10)
end
end
My view:
<%= f.text_field :username_or_email_or_chef_name_contains %>
Works fine! But, when I search based on email, I got this error:
ActiveRecord::StatementInvalid in Accounts#index
Mysql2::Error: Column 'id' in order clause is ambiguous: SELECT `accounts`.`id` AS t0_r0, `accounts`.`chef_id` AS t0_r1,...
If I take off the .includes(:chef) of account controller, works fine.
QUESTION
Why this error? For performance reasons, I wouldn't like to remove the include from account controller.
The accounts table and chefs table each have an id column, so MySQL doesn't know which of those columns it should order by. Try specifying the table name in the order by clause:
#accounts = #search.order("accounts.id desc").includes(:chef).page(params[:pagina]).per(10)

MetaSearch sort ordered column

I have a model:
class Event < ActiveRecord::Base
default_scope :order => 'date_begin'
end
There is a sort link in a view file:
= sort_link #search, :date_begin
When I'm trying to order date_begin as DESC nothing happens because the SQL query is:
SELECT * FROM events ORDER BY date_begin, date_begin DESC
How to make MetaSearch reorder this column? (I know that there is a "reorder" method in ActiveRecord but I don't know how to apply it to MetaSearch)
You can use unscoped method when you decided to use meta_search:
#search = Event.unscoped.search(params[:search])
I also wanted to use a default sort order, and didn't figure out any other way than to enforce a default order in the controller, not using any ordering scope in the model:
search = {"meta_sort" => "created_at.desc"}.merge(params[:search] || {})
#search = Photo.search(search)
The default sort order is created_at DESC, but it will be overwritten if a new sort order is received in the params. Seems to work for me.
#search = if params[:q] && params[:q][:s]
# Ransack sorting is applied - cancel default sorting
Event.reorder(nil).search(params[:q])
else
# Use default sorting
Event.search(params[:q])
end
Benefits:
1) only cancels :order scope - useful if you have .where(:deleted_at => nil).order(:date_begin) default scope.
2) uses default ordering when Ransack sorting is not applied.

Rails ActiveRecord: Find All Users Except Current User

I feel this should be very simple but my brain is short-circuiting on it. If I have an object representing the current user, and want to query for all users except the current user, how can I do this, taking into account that the current user can sometimes be nil?
This is what I am doing right now:
def index
#users = User.all
#users.delete current_user
end
What I don't like is that I am doing post-processing on the query result. Besides feeling a little wrong, I don't think this will work nicely if I convert the query over to be run with will_paginate. Any suggestions for how to do this with a query? Thanks.
It is possible to do the following in Rails 4 and up:
User.where.not(id: id)
You can wrap it in a nice scope.
scope :all_except, ->(user) { where.not(id: user) }
#users = User.all_except(current_user)
Or use a class method if you prefer:
def self.all_except(user)
where.not(id: user)
end
Both methods will return an AR relation object. This means you can chain method calls:
#users = User.all_except(current_user).paginate
You can exclude any number of users because where() also accepts an array.
#users = User.all_except([1,2,3])
For example:
#users = User.all_except(User.unverified)
And even through other associations:
class Post < ActiveRecord::Base
has_many :comments
has_many :commenters, -> { uniq }, through: :comments
end
#commenters = #post.commenters.all_except(#post.author)
See where.not() in the API Docs.
#users = (current_user.blank? ? User.all : User.find(:all, :conditions => ["id != ?", current_user.id]))
You can also create named_scope, e.g. in your model:
named_scope :without_user, lambda{|user| user ? {:conditions => ["id != ?", user.id]} : {} }
and in controller:
def index
#users = User.without_user(current_user).paginate
end
This scope will return all users when called with nil and all users except given in param in other case. The advantage of this solution is that you are free to chain this call with other named scopes or will_paginate paginate method.
Here is a shorter version:
User.all :conditions => (current_user ? ["id != ?", current_user.id] : [])
One note on GhandaL's answer - at least in Rails 3, it's worth modifying to
scope :without_user, lambda{|user| user ? {:conditions => ["users.id != ?", user.id]} : {} }
(the primary change here is from 'id != ...' to 'users.id !=...'; also scope instead of named_scope for Rails 3)
The original version works fine when simply scoping the Users table. When applying the scope to an association (e.g. team.members.without_user(current_user).... ), this change was required to clarify which table we're using for the id comparison. I saw a SQL error (using SQLite) without it.
Apologies for the separate answer...i don't yet have the reputation to comment directly on GhandaL's answer.
Very easy solution I used
#users = User.all.where("id != ?", current_user.id)
User.all.where("id NOT IN(?)", current_user.id) will through exception
undefined method where for #<Array:0x0000000aef08f8>
User.where("id NOT IN (?)", current_user.id)
Another easy way you could do it:
#users = User.all.where("id NOT IN(?)", current_user.id)
an array would be more helpful
arrayID[0]=1
arrayID[1]=3
User.where.not(id: arrayID)
User.where(:id.ne=> current_user.id)
ActiveRecord::QueryMethods#excluding (Rails 7+)
Starting from Rails 7, there is a new method ActiveRecord::QueryMethods#excluding.
A quote right from the official Rails docs:
excluding(*records)
Excludes the specified record (or collection of records) from the resulting relation. For example:
Post.excluding(post)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" != 1
Post.excluding(post_one, post_two)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)
This can also be called on associations. As with the above example, either a single record of collection thereof may be specified:
post = Post.find(1)
comment = Comment.find(2)
post.comments.excluding(comment)
# SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 AND "comments"."id" != 2
This is short-hand for .where.not(id: post.id) and .where.not(id: [post_one.id, post_two.id]).
An ArgumentError will be raised if either no records are specified, or if any of the records in the collection (if a collection is passed in) are not instances of the same model that the relation is scoping.
Also aliased as: without
Sources:
Official docs - ActiveRecord::QueryMethods#excluding
PR - Add #excluding to ActiveRecord::Relation to exclude a record (or collection of records) from the resulting relation.
What's Cooking in Rails 7?
What you are doing is deleting the current_user from the #users Array. This won't work since there isn't a delete method for arrays. What you probably want to do is this
def index
#users = User.all
#users - [current_user]
end
This will return a copy of the #users array, but with the current_user object removed (it it was contained in the array in the first place.
Note: This may not work if array subtraction is based on exact matches of objects and not the content. But it worked with strings when I tried it. Remember to enclose current_user in [] to force it into an Array.

Resources