Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add multiple sort field support in sort_link #438

Merged
merged 2 commits into from
Oct 2, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 57 additions & 20 deletions lib/ransack/helpers/form_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def search_form_for(record, options = {}, &proc)
form_for(record, options, &proc)
end

# sort_link @q, :name, [:name, 'kind ASC'], 'Player Name'
def sort_link(search, attribute, *args)
# Extract out a routing proxy for url_for scoping later
if search.is_a?(Array)
Expand All @@ -65,39 +66,75 @@ def sort_link(search, attribute, *args)
raise TypeError, "First argument must be a Ransack::Search!" unless
Search === search

# This is the field that this link represents. The direction of the sort icon (up/down arrow) will
# depend on the sort status of this field
field_name = attribute.to_s

# Determine the fields we want to sort on
sort_fields = if Array === args.first
args.shift
else
Array(field_name)
end

label = if ! args.first.try(:is_a?, Hash)
args.shift.to_s
else
Translate.attribute(field_name, :context => search.context)
end

options = args.first.is_a?(Hash) ? args.shift.dup : {}
default_order = options.delete :default_order
# If the default order is a hash of fields, duplicate it and let us access it with strings or symbols
default_order = default_order.dup.with_indifferent_access if Hash === default_order

search_params = params[search.context.search_key].presence ||
{}.with_indifferent_access

attr_name = attribute.to_s
# Find the current direction (if there is one) of the primary sort field
if existing_sort = search.sorts.detect { |s| s.name == field_name }
field_current_dir = existing_sort.dir
end

name = (
if args.size > 0 && !args.first.is_a?(Hash)
args.shift.to_s
else
Translate.attribute(attr_name, :context => search.context)
sort_params = []

Array(sort_fields).each do |sort_field|
attr_name, new_dir = sort_field.to_s.downcase.split(/\s+/)
current_dir = nil

# if the user didn't specify the sort direction, detect the previous
# sort direction on this field and reverse it
if %w{ asc desc }.none? { |d| d == new_dir }
if existing_sort = search.sorts.detect { |s| s.name == attr_name }
current_dir = existing_sort.dir
end

new_dir = if current_dir
current_dir == desc ? asc : desc
else
if Hash === default_order
default_order[attr_name] || asc
else
default_order || asc
end
end
end
)

if existing_sort = search.sorts.detect { |s| s.name == attr_name }
prev_attr, prev_dir = existing_sort.name, existing_sort.dir
sort_params << "#{attr_name} #{new_dir}"
end

options = args.first.is_a?(Hash) ? args.shift.dup : {}
default_order = options.delete :default_order
current_dir = prev_attr == attr_name ? prev_dir : nil

if current_dir
new_dir = current_dir == desc ? asc : desc
else
new_dir = default_order || asc
# if there is only one sort parameter, remove it from the array and just
# use the string as the parameter
if sort_params.size == 1
sort_params = sort_params.first
end

html_options = args.first.is_a?(Hash) ? args.shift.dup : {}
css = ['sort_link', current_dir].compact.join(' ')
css = ['sort_link', field_current_dir].compact.join(' ')
html_options[:class] = [css, html_options[:class]].compact.join(' ')
query_hash = {}
query_hash[search.context.search_key] = search_params
.merge(:s => "#{attr_name} #{new_dir}")
.merge(:s => sort_params)
options.merge!(query_hash)
options_for_url = params.merge options

Expand All @@ -108,7 +145,7 @@ def sort_link(search, attribute, *args)
end

link_to(
[ERB::Util.h(name), order_indicator_for(current_dir)]
[ERB::Util.h(label), order_indicator_for(field_current_dir)]
.compact
.join(non_breaking_space)
.html_safe,
Expand Down
139 changes: 139 additions & 0 deletions spec/ransack/helpers/form_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,145 @@ module Helpers
}
end

describe '#sort_link with multiple search_keys defined as an array' do
subject { @controller.view_context
.sort_link(
[:main_app, Person.search(:sorts => ['name desc', 'email asc'])],
:name, [:name, 'email DESC'],
:controller => 'people'
)
}
it {
should match(
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
/people\?q%5Bs%5D%5B%5D=name\+asc&amp;q%5Bs%5D%5B%5D=email+desc/
else
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
end
)
}
it {
should match /sort_link desc/
}
it {
should match /Full Name&nbsp;&#9660;/
}
end

describe '#sort_link with multiple search_keys should allow a label to be specified' do
subject { @controller.view_context
.sort_link(
[:main_app, Person.search(:sorts => ['name desc', 'email asc'])],
:name, [:name, 'email DESC'],
'Property Name',
:controller => 'people'
)
}
it {
should match /Property Name&nbsp;&#9660;/
}
end

describe '#sort_link with multiple search_keys should flip multiple fields specified without a direction' do
subject { @controller.view_context
.sort_link(
[:main_app, Person.search(:sorts => ['name desc', 'email asc'])],
:name, [:name, :email],
:controller => 'people'
)
}
it {
should match(
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
/people\?q%5Bs%5D%5B%5D=name\+asc&amp;q%5Bs%5D%5B%5D=email+desc/
else
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
end
)
}
it {
should match /sort_link desc/
}
it {
should match /Full Name&nbsp;&#9660;/
}
end

describe '#sort_link with multiple search_keys should allow a default_order to be specified' do
subject { @controller.view_context
.sort_link(
[:main_app, Person.search()],
:name, [:name, :email],
:controller => 'people',
:default_order => 'desc'
)
}
it {
should match(
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
/people\?q%5Bs%5D%5B%5D=name\+desc&amp;q%5Bs%5D%5B%5D=email+desc/
else
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
end
)
}
it {
should match /sort_link/
}
it {
should match /Full Name/
}
end

describe '#sort_link with multiple search_keys should allow multiple default_orders to be specified' do
subject { @controller.view_context
.sort_link(
[:main_app, Person.search()],
:name, [:name, :email],
:controller => 'people',
:default_order => { 'name' => 'desc', :email => 'asc' }
)
}
it {
should match(
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
/people\?q%5Bs%5D%5B%5D=name\+desc&amp;q%5Bs%5D%5B%5D=email+asc/
else
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/
end)
}
it {
should match /sort_link/
}
it {
should match /Full Name/
}
end

describe '#sort_link with multiple search_keys with multiple default_orders should not override a specified order' do
subject { @controller.view_context
.sort_link(
[:main_app, Person.search()],
:name, [:name, 'email desc'],
:controller => 'people',
:default_order => { 'name' => 'desc', :email => 'asc' }
)
}
it {
should match(
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
/people\?q%5Bs%5D%5B%5D=name\+desc&amp;q%5Bs%5D%5B%5D=email+desc/
else
/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&amp;q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/
end)
}
it {
should match /sort_link/
}
it {
should match /Full Name/
}
end
context 'view has existing parameters' do
before do
@controller.view_context.params.merge!({ :exist => 'existing' })
Expand Down