Skip to content

Commit

Permalink
Merge pull request #39 from jmkoni/add-filteriffic-to-users
Browse files Browse the repository at this point in the history
add filteriffic to user model
  • Loading branch information
jmkoni authored Jun 14, 2019
2 parents b9595a7 + 34885b1 commit e6db88c
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 41 deletions.
9 changes: 8 additions & 1 deletion app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ class UsersController < ApplicationController
# GET /users
# GET /users.json
def index
@users = User.all
(@filterrific = initialize_filterrific(
User.all,
params[:filterrific],
select_options: {
sorted_by: User.options_for_sorted_by
}
)) || return
@users = @filterrific.find.page(params[:page])
end

# GET /users/new
Expand Down
59 changes: 59 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,58 @@ class User < ApplicationRecord

validates :email, presence: true, uniqueness: { case_sensitive: false }

filterrific(
default_filter_params: { sorted_by: 'email_desc' },
available_filters: %i[
sorted_by
search_query
]
)

scope :admins, -> {
where(is_admin: true)
}

scope :search_query, ->(query) {
return nil if query.blank?

# condition query, parse into individual keywords
terms = query.downcase.split(/\s+/)
# replace "*" with "%" for wildcard searches,
# append '%', remove duplicate '%'s
terms = terms.map do |e|
('%' + e.gsub('*', '%') + '%').gsub(/%+/, '%')
end
# configure number of OR conditions for provision
# of interpolation arguments. Adjust this if you
# change the number of OR conditions.
num_or_conditions = 1
where(
terms.map do
or_clauses = [
'LOWER(users.email) LIKE ?'
].join(' OR ')
"(#{or_clauses})"
end.join(' AND '),
*terms.map { |e| [e] * num_or_conditions }.flatten
)
}

scope :sorted_by, ->(sort_option) {
# extract the sort direction from the param value.
direction = /desc$/.match?(sort_option) ? 'desc' : 'asc'
case sort_option.to_s
when /^email_/
order(Arel.sql("LOWER(users.email) #{direction}"))
when /^years_experience_/
order(Arel.sql("users.years_experience #{direction}"))
when /^admin_/
order(Arel.sql("users.is_admin #{direction}"))
else
raise(ArgumentError, "Invalid sort option: #{sort_option.inspect}")
end
}

def admin?
is_admin
end
Expand All @@ -22,6 +70,17 @@ def sha_email
sha = Digest::SHA1.new
sha.hexdigest email
end

def self.options_for_sorted_by
[
['Email (a-z)', 'email_asc'],
['Email (z-a)', 'email_desc'],
['Years Experience (lowest first)', 'years_experience_asc'],
['Years Experience (highest first)', 'years_experience_desc'],
['Admin? (false first)', 'admin_asc'],
['Admin? (true first)', 'admin_desc']
]
end
end

# == Schema Information
Expand Down
45 changes: 45 additions & 0 deletions app/views/users/_list.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<% if users.load.empty? %>
No users at this time!
<% else %>
<div id="filterrific_results">
<div class="card card-body bg-light d-block p-3 my-3">
<div class="row">
<div class="col-md-10">
<%= page_entries_info users, model: 'user', class: 'align-middle' %>
</div>
<div class="col-md-2">
<%= link_to "Reset filters", reset_filterrific_url, class: 'btn btn-secondary align-middle' %>
</div>
</div>
<%= render_filterrific_spinner %>
</div>
<table class="table table-hover table-sm table-responsive">
<thead class="thead-light">
<tr>
<th scope="col"><%= filterrific_sorting_link(@filterrific, :email) %></th>
<th scope="col"><%= filterrific_sorting_link(@filterrific, :years_experience) %></th>
<th scope="col"><%= filterrific_sorting_link(@filterrific, :admin) %></th>
<th scope="col" colspan="3"></th>
</tr>
</thead>

<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.email %></td>
<td><%= user.years_experience %></td>
<td><%= user.is_admin %></td>
<% if user.admin? %>
<td><%= link_to 'Demote', user_demote_path(user), method: :put %></td>
<% else %>
<td><%= link_to 'Promote', user_promote_path(user), method: :put %></td>
<% end %>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
<%= will_paginate users, renderer: WillPaginate::ActionView::BootstrapLinkRenderer %>
<% end %>
58 changes: 29 additions & 29 deletions app/views/users/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
<% if can? :manage, User %>
<h1>Users</h1><!--
<%= link_to 'New User', new_user_path %> -->
<table class="table table-hover table-sm table-responsive">
<thead class="thead-light">
<tr>
<th scope="col">Email</th>
<th scope="col">Years experience</th>
<th scope="col">Is admin</th>
<th scope="col" colspan="3"></th>
</tr>
</thead>

<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.email %></td>
<td><%= user.years_experience %></td>
<td><%= user.is_admin %></td>
<% if user.admin? %>
<td><%= link_to 'Demote', user_demote_path(user), method: :put %></td>
<% else %>
<td><%= link_to 'Promote', user_promote_path(user), method: :put %></td>
<% end %>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<h1>Users</h1>
<%= link_to 'New User', new_user_path %>
<br/>
<div class="card card-body bg-light">
<%= form_for_filterrific @filterrific do |f| %>
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label class="font-weight-bold">Search</label>
<%= f.text_field(
:search_query,
class: 'filterrific-periodically-observed'
) %>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label class="font-weight-bold">Sorted by</label>
<%= f.select(:sorted_by, @filterrific.select_options[:sorted_by],
{},
{ class: 'form-control' }
) %>
</div>
</div>
</div>
<% end %>
</tbody>
</table>
<% end %>
</div>
<% end %>
<%= render 'list', users: @users %>
7 changes: 7 additions & 0 deletions app/views/users/index.js.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<% js = escape_javascript(
render(
partial: 'users/list',
locals: { users: @users }
)
) %>
$("#filterrific_results").html("<%= js %>");
14 changes: 3 additions & 11 deletions spec/controllers/users_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

RSpec.describe UsersController, type: :controller do
context 'admin' do
let(:current_user) { build_stubbed(:admin) }
let(:current_user) { create(:admin) }
before do
sign_in current_user
allow(request.env['warden']).to receive(:authenticate!).and_return(current_user)
Expand All @@ -13,15 +13,7 @@

describe 'GET #index' do
before do
@users = [build_stubbed(:user), build_stubbed(:user)]
allow_any_instance_of(User).to receive(:admin?).and_return(true)
allow(User).to receive(:all).and_return(@users)
allow(User).to receive(:where).and_return(@users)
end

it 'calls all' do
expect(User).to receive(:all)
get :index
@users = [create(:user), create(:user), current_user]
end

it 'returns a success response' do
Expand All @@ -31,7 +23,7 @@

it 'assigns users to @users' do
get :index
expect(assigns(:users)).to eq(@users)
expect(assigns(:users).size).to eq(3)
end

it 'renders the index template' do
Expand Down
30 changes: 30 additions & 0 deletions spec/models/user_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,36 @@
end
end
end

describe 'search_query' do
it 'gets all users with query' do
user1 = create(:user, email: 'unicorns@gmail.com')
user2 = create(:user, email: 'oh_no@test.com')
user3 = create(:user, email: 'bye@test.com')
aggregate_failures do
expect(User.search_query('unicorns').length).to eq 1
expect(User.search_query('unicorns')).to include user1
expect(User.search_query('unicorns')).not_to include user2
expect(User.search_query('unicorns')).not_to include user3
end
end
end

describe 'sorted_by' do
it 'gets all users sorted' do
user1 = create(:user, email: 'Alphabets@test.com', is_admin: true, years_experience: 3)
user2 = create(:user, email: 'Zebra@test.com', is_admin: false, years_experience: 4)
user3 = create(:user, email: 'Cows@test.com', is_admin: false, years_experience: 5)
aggregate_failures do
expect(User.sorted_by('email_asc').first).to eq user1
expect(User.sorted_by('email_desc').first).to eq user2
expect(User.sorted_by('years_experience_asc').first).to eq user1
expect(User.sorted_by('years_experience_desc').first).to eq user3
expect(User.sorted_by('admin_desc').first).to eq user1
expect { User.sorted_by('oh_no') }.to raise_error(ArgumentError, 'Invalid sort option: "oh_no"')
end
end
end
end

context 'methods' do
Expand Down

0 comments on commit e6db88c

Please sign in to comment.