flexirails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +171 -0
  4. data/Rakefile +39 -0
  5. data/app/assets/stylesheets/flexirails.scss +55 -0
  6. data/app/helpers/flexirails_helper.rb +25 -0
  7. data/app/models/flexirails/array_view.rb +17 -0
  8. data/app/models/flexirails/view.rb +123 -0
  9. data/app/views/flexirails/_container.html.erb +28 -0
  10. data/app/views/flexirails/_navigation.html.erb +66 -0
  11. data/lib/flexirails.rb +5 -0
  12. data/lib/flexirails/engine.rb +4 -0
  13. data/lib/flexirails/version.rb +3 -0
  14. data/test/dummy/Rakefile +7 -0
  15. data/test/dummy/app/assets/javascripts/application.js +9 -0
  16. data/test/dummy/app/assets/stylesheets/application.scss +7 -0
  17. data/test/dummy/app/controllers/active_record_controller.rb +70 -0
  18. data/test/dummy/app/controllers/application_controller.rb +3 -0
  19. data/test/dummy/app/controllers/static_controller.rb +68 -0
  20. data/test/dummy/app/helpers/application_helper.rb +2 -0
  21. data/test/dummy/app/models/active_record_view.rb +23 -0
  22. data/test/dummy/app/models/person.rb +2 -0
  23. data/test/dummy/app/models/static.rb +5 -0
  24. data/test/dummy/app/models/static_view.rb +5 -0
  25. data/test/dummy/app/views/active_record/index.html.erb +7 -0
  26. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  27. data/test/dummy/app/views/static/index.html.erb +7 -0
  28. data/test/dummy/config.ru +4 -0
  29. data/test/dummy/config/application.rb +44 -0
  30. data/test/dummy/config/boot.rb +10 -0
  31. data/test/dummy/config/database.yml +25 -0
  32. data/test/dummy/config/environment.rb +5 -0
  33. data/test/dummy/config/environments/development.rb +30 -0
  34. data/test/dummy/config/environments/production.rb +60 -0
  35. data/test/dummy/config/environments/test.rb +42 -0
  36. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  37. data/test/dummy/config/initializers/inflections.rb +10 -0
  38. data/test/dummy/config/initializers/mime_types.rb +5 -0
  39. data/test/dummy/config/initializers/secret_token.rb +8 -0
  40. data/test/dummy/config/initializers/session_store.rb +8 -0
  41. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  42. data/test/dummy/config/locales/de.yml +8 -0
  43. data/test/dummy/config/locales/en.yml +8 -0
  44. data/test/dummy/config/routes.rb +60 -0
  45. data/test/dummy/db/development.sqlite3 +0 -0
  46. data/test/dummy/db/migrate/20131109204431_create_people.rb +9 -0
  47. data/test/dummy/db/schema.rb +23 -0
  48. data/test/dummy/db/test.sqlite3 +0 -0
  49. data/test/dummy/log/development.log +5482 -0
  50. data/test/dummy/log/test.log +4932 -0
  51. data/test/dummy/public/404.html +26 -0
  52. data/test/dummy/public/422.html +26 -0
  53. data/test/dummy/public/500.html +26 -0
  54. data/test/dummy/public/favicon.ico +0 -0
  55. data/test/dummy/script/rails +6 -0
  56. data/test/dummy/tmp/cache/assets/development/sass/62614c0f166e45da36cfbb2781f9667e47799670/application.scssc +0 -0
  57. data/test/dummy/tmp/cache/assets/development/sass/9178b5a65ff97d4c91c8e2380e5ac8d8eb939b91/flexirails.scssc +0 -0
  58. data/test/dummy/tmp/cache/assets/development/sass/b2a814b45c3cca5078e8af8655d3f89a5f705998/flexirails.scssc +0 -0
  59. data/test/dummy/tmp/cache/assets/development/sass/e91bee6f7bc4c18550721ba1f2d7361da7e3d2e5/application.scssc +0 -0
  60. data/test/dummy/tmp/cache/assets/development/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  61. data/test/dummy/tmp/cache/assets/development/sprockets/24585794383ac6ae2640d6c9e4affdbd +0 -0
  62. data/test/dummy/tmp/cache/assets/development/sprockets/2edad7c2ee0e34d5988da78ef799ca6c +0 -0
  63. data/test/dummy/tmp/cache/assets/development/sprockets/2ef811475ef30c8afbab72317974e714 +0 -0
  64. data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  65. data/test/dummy/tmp/cache/assets/development/sprockets/33fea692ac14771a738e10ee5d76ae96 +0 -0
  66. data/test/dummy/tmp/cache/assets/development/sprockets/352bab412d75fa19d0a07504553b59df +0 -0
  67. data/test/dummy/tmp/cache/assets/development/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  68. data/test/dummy/tmp/cache/assets/development/sprockets/5584751febc87e8b2ba4b7f38285eceb +0 -0
  69. data/test/dummy/tmp/cache/assets/development/sprockets/69e627a9d109212088a08e7938543d9d +0 -0
  70. data/test/dummy/tmp/cache/assets/development/sprockets/6e1bd95023705b5529e7ccc754a02867 +0 -0
  71. data/test/dummy/tmp/cache/assets/development/sprockets/836b5f0381caa8a96cebbee674df2708 +0 -0
  72. data/test/dummy/tmp/cache/assets/development/sprockets/85c3b8d54510866ae2cc33aedc247eb0 +0 -0
  73. data/test/dummy/tmp/cache/assets/development/sprockets/889b9afc807369ef171867cb782dd99c +0 -0
  74. data/test/dummy/tmp/cache/assets/development/sprockets/92613a75279536c4bcf4f3ba6cfde494 +0 -0
  75. data/test/dummy/tmp/cache/assets/development/sprockets/c0f4ca569d58f951461e6ccadfdd2649 +0 -0
  76. data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  77. data/test/dummy/tmp/cache/assets/development/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  78. data/test/dummy/tmp/cache/assets/development/sprockets/dcea77c9b7315ca8f48eb8357e38a219 +0 -0
  79. data/test/dummy/tmp/cache/assets/development/sprockets/e714eeaf481f5a44ddca306c4a9c7137 +0 -0
  80. data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  81. data/test/dummy/tmp/cache/assets/test/sass/9178b5a65ff97d4c91c8e2380e5ac8d8eb939b91/flexirails.scssc +0 -0
  82. data/test/dummy/tmp/cache/assets/test/sass/e91bee6f7bc4c18550721ba1f2d7361da7e3d2e5/application.scssc +0 -0
  83. data/test/dummy/tmp/cache/assets/test/sprockets/2ef811475ef30c8afbab72317974e714 +0 -0
  84. data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  85. data/test/dummy/tmp/cache/assets/test/sprockets/352bab412d75fa19d0a07504553b59df +0 -0
  86. data/test/dummy/tmp/cache/assets/test/sprockets/5584751febc87e8b2ba4b7f38285eceb +0 -0
  87. data/test/dummy/tmp/cache/assets/test/sprockets/6e1bd95023705b5529e7ccc754a02867 +0 -0
  88. data/test/dummy/tmp/cache/assets/test/sprockets/836b5f0381caa8a96cebbee674df2708 +0 -0
  89. data/test/dummy/tmp/cache/assets/test/sprockets/85c3b8d54510866ae2cc33aedc247eb0 +0 -0
  90. data/test/dummy/tmp/cache/assets/test/sprockets/889b9afc807369ef171867cb782dd99c +0 -0
  91. data/test/dummy/tmp/cache/assets/test/sprockets/92613a75279536c4bcf4f3ba6cfde494 +0 -0
  92. data/test/dummy/tmp/cache/assets/test/sprockets/c0f4ca569d58f951461e6ccadfdd2649 +0 -0
  93. data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  94. data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  95. data/test/dummy/tmp/pids/server.pid +1 -0
  96. data/test/models/flexirails/array_view_test.rb +44 -0
  97. data/test/models/flexirails/view_test.rb +46 -0
  98. data/test/rails-localization-engine_test.rb +7 -0
  99. data/test/test_helper.rb +14 -0
  100. data/test/views/flexirails/navigation_test.rb +65 -0
  101. metadata +306 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 13cd7f185547a32a1c78255b13533dde98c348a2
4
+ data.tar.gz: 7566fb2bb29edaf12737db45c5e6c71aeee5df50
5
+ SHA512:
6
+ metadata.gz: 7168c88257719d55a31be8bd847f12872ab6d4b98de945e789669e1c0a1907cc927209508c2633ba9f09e2f49415f68c21a9529504f48a3817448018bed2b284
7
+ data.tar.gz: 4e597fe640fbe4c06db3502114f54921375ba73dba8e7dfbb84ead2b789256e94e91bb631122e048a03e5137c0675454b876137beb9b94afadb9b480e6c3541b
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2011-2013 Raphael Randschau (nicolai86@me.com)
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.
@@ -0,0 +1,171 @@
1
+ # Flexirails
2
+
3
+ ORM independent table view for Ruby on Rails.
4
+ Has support for partial rendering, turbolinks and i18n as well as basic sorting and filtering facilities.
5
+ If you want more abstraction you need to build it yourself.
6
+
7
+ Look at the bright side: want to use bananadb? Go ahead.<br>
8
+ Just wrote your self CSV-In-Memory-DB? No Problem.<br>
9
+ Reading each row from /dev/null. Sure, why not?
10
+
11
+ You are in control. Just need to know how!
12
+
13
+ ### Installation
14
+
15
+ Add `gem "flexirails"` to your `Gemfile`.
16
+
17
+ ### Usage
18
+
19
+ Now you need two more steps to get going:
20
+
21
+ 1. subclass `::Flexirails::View` or `::Flexirails::ArrayView`.
22
+
23
+ ``` ruby
24
+ class PeopleView < ::Flexirails::View
25
+ def total
26
+ Person.count
27
+ end
28
+
29
+ def query offset, limit
30
+ Person.offset(offset).limit(limit)
31
+ end
32
+
33
+ def columns
34
+ %w(id name)
35
+ end
36
+ end
37
+ ```
38
+
39
+ 2. instanciate your view class in your controller:
40
+
41
+ ``` ruby
42
+ class PeopleController < ApplicationController
43
+ protected
44
+ def people_view
45
+ @people_view ||= PeopleView.new(params)
46
+ end
47
+ helper_method :people_view
48
+ end
49
+ ```
50
+
51
+ 3. render appropriate partials in your views:
52
+
53
+ ``` erb
54
+ <%= render :partial => '/flexirails/navigation', :locals => { :view => people_view } %>
55
+ <%= render_flexirails_view(people_view, { :class => 'statics' }) %>
56
+ ```
57
+
58
+ Columns are translated as `<lang>.<view_as_snake_case>.<column>`, e.g. `de.people_view.name`.
59
+
60
+ ### Advanced Usage
61
+
62
+ You can pass additional informations to your view - e.g. the current user for complex logic
63
+
64
+ ``` ruby
65
+ class PeopleView < ::Flexirails::View
66
+ attr_reader :current_user, :current_project
67
+ def initialize params, user, project
68
+ @current_user = user
69
+ @current_project = project
70
+ super params
71
+ end
72
+
73
+ def scoped
74
+ if current_user.admin?
75
+ Person.scoped
76
+ else
77
+ current_project.people
78
+ end
79
+ end
80
+
81
+ def total
82
+ scoped.count
83
+ end
84
+
85
+ def query offset, limit
86
+ scoped.offset(offset).limit(limit)
87
+ end
88
+ end
89
+ ```
90
+
91
+ By default attributes are extracted from the current row object. But you can render partials if you want to:
92
+ ``` ruby
93
+ class PeopleView < ::Flexirails::View
94
+ def total
95
+ Person.count
96
+ end
97
+
98
+ def query offset, limit
99
+ Person.offset(offset).limit(limit)
100
+ end
101
+
102
+ def columns
103
+ %w(id name actions)
104
+ end
105
+
106
+ def render_actions person, context
107
+ context.render :partial => "actions", :locals => { :person => person }
108
+ end
109
+ end
110
+ ```
111
+
112
+ You can sort your data if you want to. `order` and `direction` are sanitized by default, no need to check.
113
+ ``` ruby
114
+ class PeopleView < ::Flexirails::View
115
+ def query offset, limit
116
+ scope = if order_query?
117
+ Person.order("#{order} #{direction}")
118
+ else
119
+ Person
120
+ end
121
+ scope.offset(offset).limit(limit)
122
+ end
123
+
124
+ def sortable_columns
125
+ %w(id)
126
+ end
127
+ end
128
+ ```
129
+
130
+ You can filter your data if you want to:
131
+ ``` ruby
132
+ class PeopleView < ::Flexirails::View
133
+ attr_reader :minimum_id
134
+ def initialize params
135
+ @minimum_id = params.fetch(:minimum_id, nil)
136
+
137
+ super params
138
+ end
139
+
140
+ def scoped
141
+ if minimum_id.present?
142
+ Person.where(["id > ?", minimum_id])
143
+ else
144
+ Person.scoped
145
+ end
146
+ end
147
+
148
+ def total
149
+ scoped.count
150
+ end
151
+
152
+ def query offset, limit
153
+ scoped.offset(offset).limit(limit)
154
+ end
155
+ end
156
+ ```
157
+
158
+ ### TODO
159
+
160
+ - enhance the test suite
161
+ - refactor the small, inlined- js a little. its ugly as hell
162
+ - refactor the css. its ugly as hell as well
163
+ - maybe add a small abstraction for ActiveRecord Views
164
+
165
+ ### Contributing
166
+
167
+ 1. Fork it
168
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
169
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
170
+ 4. Push to the branch (`git push origin my-new-feature`)
171
+ 5. Create new Pull Request
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ require "bundler/gem_tasks"
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ begin
10
+ require 'rdoc/task'
11
+ rescue LoadError
12
+ require 'rdoc/rdoc'
13
+ require 'rake/rdoctask'
14
+ RDoc::Task = Rake::RDocTask
15
+ end
16
+
17
+ RDoc::Task.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'Flexirails'
20
+ rdoc.options << '--line-numbers'
21
+ rdoc.rdoc_files.include('README.rdoc')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+
26
+
27
+ Bundler::GemHelper.install_tasks
28
+
29
+ require 'rake/testtask'
30
+
31
+ Rake::TestTask.new(:test) do |t|
32
+ t.libs << 'lib'
33
+ t.libs << 'test'
34
+ t.pattern = 'test/**/*_test.rb'
35
+ t.verbose = false
36
+ end
37
+
38
+
39
+ task :default => :test
@@ -0,0 +1,55 @@
1
+ .filter {
2
+ .row .columns {
3
+ height: 32px !important;
4
+ line-height: 32px !important;
5
+ margin-bottom: 3px;
6
+ }
7
+ margin-bottom: 5px;
8
+ }
9
+
10
+ div.flexirails {
11
+ overflow: scroll;
12
+ }
13
+
14
+ nav.flexirails {
15
+ div {
16
+ display: inline-block;
17
+ }
18
+ input {
19
+ display: inline-block;
20
+ }
21
+ .page {
22
+ input {
23
+ width: 50px;
24
+ }
25
+ }
26
+ svg {
27
+ fill: white;
28
+ }
29
+ .pagination a {
30
+ height: 32px;
31
+ line-height: 34px;
32
+ display: inline-block;
33
+ vertical-align: middle;
34
+ }
35
+ }
36
+
37
+ .flexirails table {
38
+ width: 100%;
39
+ th {
40
+ text-align: left;
41
+ }
42
+ td {
43
+ padding: 5px;
44
+ }
45
+ td.actions {
46
+ span {
47
+ display: inline-block;
48
+ }
49
+
50
+ a {
51
+ min-width: 32px;
52
+ min-height: 32px;
53
+ }
54
+ }
55
+ }
@@ -0,0 +1,25 @@
1
+ require 'url_plumber'
2
+
3
+ module FlexirailsHelper
4
+ def pagination_first_icon
5
+ return '<svg version="1.2" baseProfile="tiny" id="Navigation_first" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 512 512" overflow="inherit" xml:space="preserve"> <path d="M186.178,256.243l211.583,166.934V89.312L186.178,256.243z M112.352,422.512h66.179V89.975h-66.179V422.512z"/> </svg>'
6
+ end
7
+ def pagination_prev_icon
8
+ return '<svg version="1.2" baseProfile="tiny" id="Navigation_left" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 512 512" overflow="inherit" xml:space="preserve"> <polygon points="148.584,255.516 360.168,88.583 360.166,422.445 "/> </svg>'
9
+ end
10
+ def pagination_last_icon
11
+ return '<svg version="1.2" baseProfile="tiny" id="Navigation_last" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 512 512" overflow="inherit" xml:space="preserve"> <path d="M111.708,424.514l211.581-166.927L111.708,90.654V424.514z M330.935,87.311v332.544h66.173V87.311H330.935z"/> </svg>'
12
+ end
13
+ def pagination_next_icon
14
+ return '<svg version="1.2" baseProfile="tiny" id="Navigation_right" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16px" height="16px" viewBox="0 0 512 512" overflow="inherit" xml:space="preserve"> <polygon points="360.124,255.513 148.535,422.442 148.537,88.58 "/> </svg>'
15
+ end
16
+ def render_flexirails_view view, opts = {}
17
+ return render partial: '/flexirails/container', locals: { view: view, table_class: opts.fetch(:class) { "" } }
18
+ end
19
+ def url_plumb(key, value)
20
+ url_for(::UrlPlumber::Plumber.new(params).plumb(key => value))
21
+ end
22
+ def url_batch_plumb(hash)
23
+ url_for(::UrlPlumber::Plumber.new(params).plumb(hash))
24
+ end
25
+ end
@@ -0,0 +1,17 @@
1
+ module Flexirails
2
+ class ArrayView < ::Flexirails::View
3
+ attr_reader :items
4
+ def initialize params, items
5
+ @items = items
6
+ super params
7
+ end
8
+
9
+ def query offset, limit
10
+ items[offset..(offset+limit-1)]
11
+ end
12
+
13
+ def total
14
+ items.size
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,123 @@
1
+ module Flexirails
2
+ class View
3
+ attr_reader :offset, :limit, :current_page, :per_page, :order, :direction, :params
4
+
5
+ def initialize params
6
+ @params = params
7
+ pagination = params.fetch(:pagination) { params || Hash.new }
8
+
9
+ @current_page = pagination.fetch(:current_page) { 1 }.to_i
10
+ @per_page = pagination.fetch(:per_page) { 25 }.to_i
11
+ @order = sanitize(pagination.fetch(:order) { nil })
12
+ @direction = sanitize_direction(pagination.fetch(:direction) { nil })
13
+
14
+ if @current_page > total_page_count
15
+ @current_page = 1
16
+ end
17
+
18
+ @offset = (current_page-1) * per_page
19
+ @limit = per_page
20
+ end
21
+
22
+ def next_pagination_direction column
23
+ if order == column
24
+ if direction == "DESC"
25
+ nil
26
+ else
27
+ if direction == nil
28
+ "ASC"
29
+ else
30
+ "DESC"
31
+ end
32
+ end
33
+ else
34
+ "ASC"
35
+ end
36
+ end
37
+
38
+ def total_page_count
39
+ return (self.total.to_f / self.per_page.to_f).ceil
40
+ end
41
+
42
+ def total
43
+ raise "ImplementationMissing"
44
+ end
45
+
46
+ def query offset = self.offset, limit = self.limit
47
+ raise "ImplementationMissing"
48
+ end
49
+
50
+ def has_prev_path
51
+ return self.current_page > 1
52
+ end
53
+ def has_next_path
54
+ return self.current_page < total_page_count
55
+ end
56
+
57
+ def sortable_columns
58
+ return %w()
59
+ end
60
+
61
+ def columns
62
+ sortable_columns
63
+ end
64
+
65
+ def order_query?
66
+ return order.present? && direction.present?
67
+ end
68
+ alias_method :order_results?, :order_query?
69
+
70
+ def rows
71
+ return @rows ||= query(offset, limit)
72
+ end
73
+
74
+ def i18n_scope clazz = self.class
75
+ return clazz.name.tableize.singularize.gsub('/','.')
76
+ end
77
+
78
+ def i18n_default name
79
+ scopes = []
80
+ clazz = self.class
81
+ clazz.ancestors.each do |ancestor|
82
+ break if ancestor == Object
83
+ scopes << [i18n_scope(ancestor),name].compact.join('.').to_sym
84
+ end
85
+ return scopes
86
+ end
87
+
88
+ def t name, args = {}
89
+ I18n.t([i18n_scope,name].compact.join('.'), { default: i18n_default(name) }.merge(args))
90
+ end
91
+
92
+ def render_column column, row, context
93
+ method_to_call = "render_#{column.gsub(/\./, '_')}"
94
+ if self.respond_to?(method_to_call.to_sym)
95
+ return self.send method_to_call.to_sym, row, context
96
+ else
97
+ parts = column.split('.').map(&:to_sym)
98
+ object = row
99
+ parts.each do |part|
100
+ object = object.send(part)
101
+ end
102
+ return object
103
+ end
104
+ end
105
+
106
+ def pagination_hash
107
+ return {
108
+ current_page: self.current_page,
109
+ per_page: self.per_page,
110
+ order: self.order,
111
+ direction: self.direction
112
+ }
113
+ end
114
+
115
+ private
116
+ def sanitize_direction direction
117
+ return %w(ASC DESC).include?(direction) ? direction : nil
118
+ end
119
+ def sanitize attribute
120
+ return columns.include?(attribute) ? attribute : nil
121
+ end
122
+ end
123
+ end