magic_grid 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +44 -0
- data/lib/assets/javascripts/magic_grid.js +154 -0
- data/lib/locales/en.yml +7 -0
- data/lib/magic_grid.rb +7 -0
- data/lib/magic_grid/definition.rb +212 -0
- data/lib/magic_grid/engine.rb +14 -0
- data/lib/magic_grid/helpers.rb +243 -0
- data/lib/magic_grid/railtie.rb +9 -0
- data/lib/magic_grid/version.rb +3 -0
- data/lib/tasks/magic_grid_tasks.rake +4 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/javascripts/posts.js +8 -0
- data/test/dummy/app/assets/javascripts/users.js +2 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/assets/stylesheets/posts.css +4 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/test/dummy/app/assets/stylesheets/users.css +4 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/posts_controller.rb +104 -0
- data/test/dummy/app/controllers/users_controller.rb +84 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/posts_helper.rb +2 -0
- data/test/dummy/app/helpers/users_helper.rb +2 -0
- data/test/dummy/app/models/post.rb +6 -0
- data/test/dummy/app/models/user.rb +4 -0
- data/test/dummy/app/views/layouts/application.html.erb +19 -0
- data/test/dummy/app/views/posts/_form.html.erb +33 -0
- data/test/dummy/app/views/posts/by_user.html.erb +18 -0
- data/test/dummy/app/views/posts/edit.html.erb +6 -0
- data/test/dummy/app/views/posts/index.html.erb +76 -0
- data/test/dummy/app/views/posts/new.html.erb +5 -0
- data/test/dummy/app/views/posts/show.html.erb +25 -0
- data/test/dummy/app/views/users/_form.html.erb +25 -0
- data/test/dummy/app/views/users/edit.html.erb +6 -0
- data/test/dummy/app/views/users/index.html.erb +42 -0
- data/test/dummy/app/views/users/new.html.erb +5 -0
- data/test/dummy/app/views/users/show.html.erb +15 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +40 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +68 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20111025191809_create_users.rb +10 -0
- data/test/dummy/db/migrate/20111025195229_create_posts.rb +13 -0
- data/test/dummy/db/schema.rb +34 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +49565 -0
- data/test/dummy/log/passenger.3000.log +362 -0
- data/test/dummy/log/test.log +77 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/test/dummy/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/test/dummy/public/css/smoothness/jquery-ui-1.8.18.custom.css +565 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/test/fixtures/posts.yml +23 -0
- data/test/dummy/test/fixtures/users.yml +15 -0
- data/test/dummy/test/functional/posts_controller_test.rb +49 -0
- data/test/dummy/test/functional/users_controller_test.rb +50 -0
- data/test/dummy/test/integration/spider_test.rb +27 -0
- data/test/dummy/test/unit/helpers/posts_helper_test.rb +4 -0
- data/test/dummy/test/unit/helpers/users_helper_test.rb +4 -0
- data/test/dummy/test/unit/post_test.rb +7 -0
- data/test/dummy/test/unit/user_test.rb +7 -0
- data/test/magic_grid_test.rb +11 -0
- data/test/test_helper.rb +17 -0
- metadata +333 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2011 YOURNAME
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
= MagicGrid
|
|
2
|
+
|
|
3
|
+
Takes a collection and creates a paginated table of using a supplied column definition.
|
|
4
|
+
|
|
5
|
+
== Basic Usage
|
|
6
|
+
|
|
7
|
+
In your +Gemfile+:
|
|
8
|
+
|
|
9
|
+
gem 'magic_grid', :git => 'git://github.com/rmg/magic_grid.git'
|
|
10
|
+
|
|
11
|
+
In your view:
|
|
12
|
+
|
|
13
|
+
<%= magic_grid(@posts, [:title, :author]) %>
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
== Development
|
|
17
|
+
|
|
18
|
+
To test, <code>(cd test/dummy && rake RAILS_ENV=test db:create db:migrate) && rake test</code>
|
|
19
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'MagicGrid'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
Bundler::GemHelper.install_tasks
|
|
26
|
+
|
|
27
|
+
require 'rake/testtask'
|
|
28
|
+
|
|
29
|
+
Rake::TestTask.new(:tests) do |t|
|
|
30
|
+
t.libs << 'lib'
|
|
31
|
+
t.libs << 'test'
|
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
|
33
|
+
t.verbose = false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
require 'rspec/core/rake_task'
|
|
37
|
+
|
|
38
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
|
39
|
+
t.pattern = 'spec/**/*_spec.rb'
|
|
40
|
+
t.verbose = false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
task :test => [:spec, :tests]
|
|
44
|
+
task :default => :test
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// vim: ts=4 et sw=4 sts=4
|
|
2
|
+
|
|
3
|
+
$(function () {
|
|
4
|
+
// Micro-plugin for positioning the cursor
|
|
5
|
+
$.fn.setCursor = $.fn.setCursor || function(pos) {
|
|
6
|
+
return this.each(function() {
|
|
7
|
+
if (this.setSelectionRange) {
|
|
8
|
+
this.focus();
|
|
9
|
+
this.setSelectionRange(pos, pos);
|
|
10
|
+
} else if (this.createTextRange) {
|
|
11
|
+
var range = this.createTextRange();
|
|
12
|
+
range.collapse(true);
|
|
13
|
+
range.moveEnd('character', pos);
|
|
14
|
+
range.moveStart('character', pos);
|
|
15
|
+
range.select();
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Micro-plugin for getting the position of the cursor
|
|
21
|
+
$.fn.getCursor = $.fn.getCursor || function () {
|
|
22
|
+
// IE doesn't have selectionStart, and I'm too lazy to implement
|
|
23
|
+
// the IE specific version of this, so we'll just assume
|
|
24
|
+
// the cursor is at the end of the input for this case.
|
|
25
|
+
if (typeof this[0].selectionStart === 'undefined') {
|
|
26
|
+
return this.value.length;
|
|
27
|
+
} else {
|
|
28
|
+
return this[0].selectionStart;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Micro-plugin for collecting all the listener and search inputs
|
|
33
|
+
$.fn.getGridParams = $.fn.getGridParams || function (extras) {
|
|
34
|
+
var listeners = this.data("listeners"),
|
|
35
|
+
params = $.extend({}, {'magic_grid_id' : this.attr('id')}, extras || {}),
|
|
36
|
+
$input, param_key, listened_id;
|
|
37
|
+
for (listened_id in listeners) {
|
|
38
|
+
param_key = listeners[listened_id];
|
|
39
|
+
$input = $("#" + listened_id);
|
|
40
|
+
if ($input.is(":checkbox")) {
|
|
41
|
+
params[param_key] = $input.is(":checked");
|
|
42
|
+
} else if ($input.is("select")) {
|
|
43
|
+
params[param_key] = $input.find("option:selected").val();
|
|
44
|
+
} else {
|
|
45
|
+
params[param_key] = $input.val();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (this.is("[data-searcher]")) {
|
|
49
|
+
params[this.attr('id') + '_q'] = $("#" + this.data("searcher")).val();
|
|
50
|
+
}
|
|
51
|
+
return $.extend({}, params);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Default handler for ajax events that displays 'Loading...' in
|
|
55
|
+
// the head of the table.
|
|
56
|
+
$(".magic_grid[data-default-ajax-handler=true]").on("magic_grid:loading", function (e) {
|
|
57
|
+
$(".magic_grid_spinner", this).
|
|
58
|
+
addClass("ui-icon-spinner").
|
|
59
|
+
html("<em>Loading...</em>");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
$(".magic_grid[data-remote=true]").on( "click", ".pagination a, .sorter a", function (e) {
|
|
63
|
+
var $grid = $(e.delegateTarget),
|
|
64
|
+
url = this.href;
|
|
65
|
+
$grid.trigger("magic_grid:loading");
|
|
66
|
+
$grid.load(url + ' #' + $grid.attr('id') + " > *", function () {
|
|
67
|
+
$grid.trigger("magic_grid:loaded");
|
|
68
|
+
});
|
|
69
|
+
return false;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
$(".magic_grid[data-searcher]").each( function () {
|
|
73
|
+
var $grid = $(this),
|
|
74
|
+
grid_id = this.id,
|
|
75
|
+
input_id = "#" + $grid.data("searcher"),
|
|
76
|
+
live = $grid.data("live-search"),
|
|
77
|
+
// keydown seems to be the only reliable and portable way to capture
|
|
78
|
+
// things like backspace and delete
|
|
79
|
+
events = (live ? "keydown change search" : "change search"),
|
|
80
|
+
timer = null,
|
|
81
|
+
internal = ($grid.has(input_id).length > 0),
|
|
82
|
+
listener = (internal ? $grid : $(input_id).parent()),
|
|
83
|
+
minLength = $(input_id).data("min-length") || 3;
|
|
84
|
+
listener.on(events, input_id, function (e) {
|
|
85
|
+
var is_manual = (e.type == 'search' ||
|
|
86
|
+
e.type == 'change' ||
|
|
87
|
+
(e.type == 'keydown' && e.which == '13')),
|
|
88
|
+
base_url = $grid.data("current") || "",
|
|
89
|
+
//40 wpm typists == 280ms/keystroke
|
|
90
|
+
//90 wpm typists == 120ms/keystroke
|
|
91
|
+
delay = is_manual ? 1 : (parseInt(live) || 500);
|
|
92
|
+
clearTimeout(timer);
|
|
93
|
+
timer = setTimeout(function () {
|
|
94
|
+
var $input = $(input_id),
|
|
95
|
+
current = $input.data("current"),
|
|
96
|
+
value = $input.val(),
|
|
97
|
+
length = value.length,
|
|
98
|
+
url = base_url + "?",
|
|
99
|
+
relevant = is_manual || (value != current && (length >= minLength || length == 0)),
|
|
100
|
+
pos = $input.getCursor();
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
url += $.param($grid.getGridParams());
|
|
103
|
+
if (relevant) {
|
|
104
|
+
$grid.trigger("magic_grid:loading");
|
|
105
|
+
if ($grid.data("remote")) {
|
|
106
|
+
if (internal && live && !is_manual) {
|
|
107
|
+
$(input_id).prop('disabled', true);
|
|
108
|
+
}
|
|
109
|
+
$grid.load(url + ' #' + grid_id + ' > *', function () {
|
|
110
|
+
// Move the cursor back to where it was,
|
|
111
|
+
// less surprising that way.
|
|
112
|
+
// EXCEPT FOR IE, that is
|
|
113
|
+
$grid.trigger("magic_grid:loaded");
|
|
114
|
+
if (internal) {
|
|
115
|
+
$(input_id).setCursor(pos);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
} else {
|
|
119
|
+
window.location = url;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}, delay);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
$(".magic_grid[data-listeners]").each( function () {
|
|
127
|
+
var $grid = $(this),
|
|
128
|
+
listeners = $grid.data("listeners"),
|
|
129
|
+
grid_id = this.id
|
|
130
|
+
base_url = $grid.data("current"),
|
|
131
|
+
handler = function (change) {
|
|
132
|
+
var url = base_url + "?" + $.param($grid.getGridParams());
|
|
133
|
+
$grid.trigger("magic_grid:loading");
|
|
134
|
+
if ($grid.data("remote")) {
|
|
135
|
+
$grid.load(url + ' #' + grid_id + " > *", function () {
|
|
136
|
+
$grid.trigger("magic_grid:loaded");
|
|
137
|
+
});
|
|
138
|
+
} else {
|
|
139
|
+
window.location = url;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
for (var k in listeners) {
|
|
143
|
+
if ($("#" + k).hasClass("ready")) {
|
|
144
|
+
$("#" + k).on("change", {"field": listeners[k]}, handler);
|
|
145
|
+
} else {
|
|
146
|
+
$("#" + k).on("ready", {"field": listeners[k]}, function (ready) {
|
|
147
|
+
$(this).on("change", ready.data, handler);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
$(".magic_grid").trigger("magic_grid:loaded");
|
|
154
|
+
});
|
data/lib/locales/en.yml
ADDED
data/lib/magic_grid.rb
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
require 'will_paginate/view_helpers/action_view'
|
|
2
|
+
|
|
3
|
+
module MagicGrid
|
|
4
|
+
class Definition
|
|
5
|
+
include WillPaginate::ActionView
|
|
6
|
+
attr_accessor :columns, :collection, :magic_id, :options, :params, :accepted,
|
|
7
|
+
:current_sort_col, :current_order, :default_order
|
|
8
|
+
|
|
9
|
+
DEFAULTS = {
|
|
10
|
+
:class => [],
|
|
11
|
+
:top_pager => false,
|
|
12
|
+
:bottom_pager => true,
|
|
13
|
+
:remote => false,
|
|
14
|
+
:per_page => 30,
|
|
15
|
+
:searchable => false,
|
|
16
|
+
:search_method => :search,
|
|
17
|
+
:min_search_length => 3,
|
|
18
|
+
:id => false,
|
|
19
|
+
:searcher => false,
|
|
20
|
+
:needs_searcher => false,
|
|
21
|
+
:live_search => false,
|
|
22
|
+
:current_search => nil,
|
|
23
|
+
:listeners => {},
|
|
24
|
+
:listener_handler => nil,
|
|
25
|
+
:default_col => 0,
|
|
26
|
+
:default_order => :asc,
|
|
27
|
+
:empty_header => false,
|
|
28
|
+
:empty_footer => false,
|
|
29
|
+
:post_filter => false,
|
|
30
|
+
:collection_post_filter? => true,
|
|
31
|
+
:default_ajax_handler => true,
|
|
32
|
+
:search_button => false,
|
|
33
|
+
:searcher_size => nil,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def self.runtime_defaults
|
|
37
|
+
# run these lazily to catch any late I18n path changes
|
|
38
|
+
DEFAULTS.merge(
|
|
39
|
+
:if_empty => I18n.t("magic_grid.no_results").capitalize, # "No results found."
|
|
40
|
+
:searcher_label => I18n.t("magic_grid.search.label").capitalize + ': ', # "Search: "
|
|
41
|
+
:searcher_tooltip =>I18n.t("magic_grid.search.tooltip"), # "type.. + <return>"
|
|
42
|
+
:searcher_button =>I18n.t("magic_grid.search.button").capitalize, # "Search"
|
|
43
|
+
)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def initialize(cols_or_opts, collection = nil, controller = nil, opts = {})
|
|
47
|
+
if cols_or_opts.is_a? Hash
|
|
48
|
+
@options = self.class.runtime_defaults.merge(cols_or_opts.reject {|k| k == :cols})
|
|
49
|
+
@columns = cols_or_opts.fetch(:cols, [])
|
|
50
|
+
elsif cols_or_opts.is_a? Array
|
|
51
|
+
@options = self.class.runtime_defaults.merge opts
|
|
52
|
+
@columns = cols_or_opts
|
|
53
|
+
else
|
|
54
|
+
raise "I have no idea what that is, but it's not a Hash or an Array"
|
|
55
|
+
end
|
|
56
|
+
@default_order = @options[:default_order]
|
|
57
|
+
@params = controller.try(:params) || {}
|
|
58
|
+
@collection = collection
|
|
59
|
+
begin
|
|
60
|
+
#if @collection.respond_to? :table
|
|
61
|
+
table_name = @collection.quoted_table_name
|
|
62
|
+
table_columns = @collection.table.columns.map {|c| c.name}
|
|
63
|
+
rescue
|
|
64
|
+
Rails.logger.debug "Given collection doesn't respond to :table well"
|
|
65
|
+
table_name = nil
|
|
66
|
+
table_columns = @columns.each_index.to_a
|
|
67
|
+
end
|
|
68
|
+
i = 0
|
|
69
|
+
hash = []
|
|
70
|
+
@columns.map! do |c|
|
|
71
|
+
if c.is_a? Symbol
|
|
72
|
+
c = {:col => c}
|
|
73
|
+
elsif c.is_a? String
|
|
74
|
+
c = {:label => c}
|
|
75
|
+
end
|
|
76
|
+
c[:id] = i
|
|
77
|
+
i += 1
|
|
78
|
+
if c.key?(:col) and c[:col].is_a?(Symbol) and table_columns.include?(c[:col])
|
|
79
|
+
c[:sql] = "#{table_name}.#{@collection.connection.quote_column_name(c[:col].to_s)}" unless c.key?(:sql)
|
|
80
|
+
end
|
|
81
|
+
c[:label] = c[:col].to_s.titleize if not c.key? :label
|
|
82
|
+
hash << c[:label]
|
|
83
|
+
c
|
|
84
|
+
end
|
|
85
|
+
if @options[:id]
|
|
86
|
+
@magic_id = @options[:id]
|
|
87
|
+
else
|
|
88
|
+
@magic_id = hash.join.hash.abs.to_s(36)
|
|
89
|
+
@magic_id << @collection.to_sql.hash.abs.to_s(36) if @collection.respond_to? :to_sql
|
|
90
|
+
end
|
|
91
|
+
@current_sort_col = sort_col_i = param(:col, @options[:default_col]).to_i
|
|
92
|
+
if @collection.respond_to?(:order) and @columns.count > sort_col_i and @columns[sort_col_i].has_key?(:sql)
|
|
93
|
+
sort_col = @columns[sort_col_i][:sql]
|
|
94
|
+
@current_order = order(param(:order, @default_order))
|
|
95
|
+
sort_dir = order_sql(@current_order)
|
|
96
|
+
@collection = @collection.order("#{sort_col} #{sort_dir}")
|
|
97
|
+
else
|
|
98
|
+
Rails.logger.debug "#{self.class.name}: Ignoring sorting on non-AR collection"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
@options[:searchable] = [] if @options[:searchable] and not @options[:searchable].kind_of? Array
|
|
102
|
+
|
|
103
|
+
@accepted = [:action, :controller, param_key(:page)]
|
|
104
|
+
@accepted << param_key(:q) if @options[:searchable]
|
|
105
|
+
@accepted += @options[:listeners].values
|
|
106
|
+
|
|
107
|
+
if @collection.respond_to?(:where) or @options[:listener_handler].respond_to?(:call)
|
|
108
|
+
if @options[:listener_handler].respond_to? :call
|
|
109
|
+
@collection = @options[:listener_handler].call(@collection)
|
|
110
|
+
else
|
|
111
|
+
@options[:listeners].each_pair do |key, value|
|
|
112
|
+
if @params[value] and not @params[value].to_s.empty?
|
|
113
|
+
@collection = @collection.where(key => @params[value])
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
else
|
|
118
|
+
unless @options[:listeners].empty?
|
|
119
|
+
Rails.logger.warn "#{self.class.name}: Ignoring listener on dumb collection"
|
|
120
|
+
@options[:listeners] = {}
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
@options[:current_search] ||= param(:q)
|
|
124
|
+
if (@collection.respond_to?(:where) or
|
|
125
|
+
(@options[:search_method] and @collection.respond_to?(@options[:search_method])))
|
|
126
|
+
if param(:q) and not param(:q).empty? and @options[:searchable]
|
|
127
|
+
orig_collection = @collection
|
|
128
|
+
begin
|
|
129
|
+
@collection = @collection.__send__(@options[:search_method], param(:q))
|
|
130
|
+
rescue
|
|
131
|
+
Rails.logger.debug "Given collection doesn't respond to #{@options[:search_method]} well"
|
|
132
|
+
@collection = orig_collection
|
|
133
|
+
search_cols = @options[:searchable].map do |searchable|
|
|
134
|
+
case searchable
|
|
135
|
+
when Symbol
|
|
136
|
+
known = @columns.find {|col| col[:col] == searchable}
|
|
137
|
+
if known and known.key?(:sql)
|
|
138
|
+
known[:sql]
|
|
139
|
+
else
|
|
140
|
+
"#{table_name}.#{@collection.connection.quote_column_name(searchable.to_s)}"
|
|
141
|
+
end
|
|
142
|
+
when Integer
|
|
143
|
+
@columns[searchable][:sql]
|
|
144
|
+
when String
|
|
145
|
+
searchable
|
|
146
|
+
else
|
|
147
|
+
raise "Searchable must be identifiable"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
unless search_cols.empty?
|
|
151
|
+
begin
|
|
152
|
+
clauses = search_cols.map {|c| c << " LIKE :search" }.join(" OR ")
|
|
153
|
+
@collection = @collection.where(clauses, {:search => "%#{param(:q)}%"})
|
|
154
|
+
rescue
|
|
155
|
+
Rails.logger.debug "Given collection doesn't respond to :where well"
|
|
156
|
+
@collection = orig_collection
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
else
|
|
162
|
+
if @options[:searchable] or param(:q)
|
|
163
|
+
Rails.logger.warn "#{self.class.name}: Ignoring searchable fields on non-AR collection"
|
|
164
|
+
end
|
|
165
|
+
@options[:searchable] = false
|
|
166
|
+
end
|
|
167
|
+
if not @options[:searcher] and @options[:searchable]
|
|
168
|
+
@options[:needs_searcher] = true
|
|
169
|
+
@options[:searcher] = param_key(:searcher)
|
|
170
|
+
end
|
|
171
|
+
# Do collection filter first, may convert from AR to Array
|
|
172
|
+
if @options[:collection_post_filter?] and @collection.respond_to?(:post_filter)
|
|
173
|
+
@collection = @collection.post_filter(controller)
|
|
174
|
+
end
|
|
175
|
+
if @options[:post_filter] and @options[:post_filter].respond_to?(:call)
|
|
176
|
+
@collection = @options[:post_filter].call(@collection)
|
|
177
|
+
end
|
|
178
|
+
# Paginate at the very end, after all sorting, filtering, etc..
|
|
179
|
+
if @options[:per_page]
|
|
180
|
+
@collection = @collection.paginate(:page => param(:page, 1),
|
|
181
|
+
:per_page => @options[:per_page])
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def param_key(key)
|
|
186
|
+
"#{@magic_id}_#{key}".to_sym
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def param(key, default=nil)
|
|
190
|
+
@params.fetch(param_key(key), default)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def base_params
|
|
194
|
+
@params.select { |k,_| accepted.include? k.to_sym }.merge :magic_grid_id => @magic_id
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def order(something)
|
|
198
|
+
case something
|
|
199
|
+
when 1, "1", :desc, :DESC, "desc", "DESC"
|
|
200
|
+
1
|
|
201
|
+
#when 0, "0", :asc, :ASC, "asc", "ASC"
|
|
202
|
+
# 0
|
|
203
|
+
else
|
|
204
|
+
0
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def order_sql(something)
|
|
209
|
+
["ASC", "DESC"][order(something)]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|