ajax-datatables-rails 0.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +8 -0
  3. data/.github/workflows/ci.yml +128 -0
  4. data/.gitignore +23 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +58 -0
  7. data/Appraisals +28 -0
  8. data/CHANGELOG.md +102 -7
  9. data/Gemfile +4 -1
  10. data/Guardfile +16 -0
  11. data/LICENSE +17 -18
  12. data/README.md +595 -404
  13. data/Rakefile +4 -2
  14. data/ajax-datatables-rails.gemspec +41 -25
  15. data/appraisal.yml +56 -0
  16. data/bin/_guard-core +29 -0
  17. data/bin/appraisal +29 -0
  18. data/bin/bundle +114 -0
  19. data/bin/guard +29 -0
  20. data/bin/rackup +27 -0
  21. data/bin/rake +29 -0
  22. data/bin/rspec +29 -0
  23. data/bin/rubocop +29 -0
  24. data/config.ru +7 -0
  25. data/doc/migrate.md +97 -0
  26. data/doc/webpack.md +55 -0
  27. data/gemfiles/rails_5.2.8.gemfile +21 -0
  28. data/gemfiles/rails_6.0.6.gemfile +21 -0
  29. data/gemfiles/rails_6.1.7.gemfile +21 -0
  30. data/gemfiles/rails_7.0.4.gemfile +21 -0
  31. data/lib/ajax-datatables-rails/active_record.rb +7 -0
  32. data/lib/ajax-datatables-rails/base.rb +114 -149
  33. data/lib/ajax-datatables-rails/datatable/column/date_filter.rb +68 -0
  34. data/lib/ajax-datatables-rails/datatable/column/order.rb +29 -0
  35. data/lib/ajax-datatables-rails/datatable/column/search.rb +125 -0
  36. data/lib/ajax-datatables-rails/datatable/column.rb +123 -0
  37. data/lib/ajax-datatables-rails/datatable/datatable.rb +91 -0
  38. data/lib/ajax-datatables-rails/datatable/simple_order.rb +59 -0
  39. data/lib/ajax-datatables-rails/datatable/simple_search.rb +23 -0
  40. data/lib/ajax-datatables-rails/datatable.rb +6 -0
  41. data/lib/ajax-datatables-rails/error.rb +9 -0
  42. data/lib/ajax-datatables-rails/orm/active_record.rb +60 -0
  43. data/lib/ajax-datatables-rails/orm.rb +6 -0
  44. data/lib/ajax-datatables-rails/version.rb +15 -1
  45. data/lib/ajax-datatables-rails.rb +11 -7
  46. data/lib/generators/rails/datatable_generator.rb +11 -22
  47. data/lib/generators/rails/templates/datatable.rb +13 -15
  48. data/spec/ajax-datatables-rails/base_spec.rb +223 -0
  49. data/spec/ajax-datatables-rails/datatable/column_spec.rb +222 -0
  50. data/spec/ajax-datatables-rails/datatable/datatable_spec.rb +127 -0
  51. data/spec/ajax-datatables-rails/datatable/simple_order_spec.rb +62 -0
  52. data/spec/ajax-datatables-rails/datatable/simple_search_spec.rb +19 -0
  53. data/spec/ajax-datatables-rails/orm/active_record_filter_records_spec.rb +639 -0
  54. data/spec/ajax-datatables-rails/orm/active_record_paginate_records_spec.rb +67 -0
  55. data/spec/ajax-datatables-rails/orm/active_record_sort_records_spec.rb +80 -0
  56. data/spec/dummy/app/assets/config/manifest.js +0 -0
  57. data/spec/dummy/config/database.yml +25 -0
  58. data/spec/dummy/config/routes.rb +5 -0
  59. data/spec/dummy/config/storage.yml +3 -0
  60. data/spec/dummy/db/schema.rb +13 -0
  61. data/spec/dummy/log/.gitignore +1 -0
  62. data/spec/dummy/public/favicon.ico +0 -0
  63. data/spec/factories/user.rb +11 -0
  64. data/spec/install_oracle.sh +18 -0
  65. data/spec/spec_helper.rb +85 -6
  66. data/spec/support/datatables/complex_datatable.rb +33 -0
  67. data/spec/support/datatables/complex_datatable_array.rb +16 -0
  68. data/spec/support/datatables/datatable_cond_date.rb +7 -0
  69. data/spec/support/datatables/datatable_cond_numeric.rb +53 -0
  70. data/spec/support/datatables/datatable_cond_proc.rb +13 -0
  71. data/spec/support/datatables/datatable_cond_string.rb +43 -0
  72. data/spec/support/datatables/datatable_cond_unknown.rb +7 -0
  73. data/spec/support/datatables/datatable_custom_column.rb +17 -0
  74. data/spec/support/datatables/datatable_order_nulls_last.rb +7 -0
  75. data/spec/support/helpers/params.rb +80 -0
  76. data/spec/support/models/user.rb +7 -0
  77. metadata +249 -48
  78. data/lib/ajax-datatables-rails/config.rb +0 -25
  79. data/lib/ajax-datatables-rails/extensions/kaminari.rb +0 -12
  80. data/lib/ajax-datatables-rails/extensions/simple_paginator.rb +0 -12
  81. data/lib/ajax-datatables-rails/extensions/will_paginate.rb +0 -12
  82. data/lib/ajax-datatables-rails/models.rb +0 -6
  83. data/lib/generators/datatable/config_generator.rb +0 -17
  84. data/lib/generators/datatable/templates/ajax_datatables_rails_config.rb +0 -7
  85. data/spec/ajax-datatables-rails/ajax_datatables_rails_spec.rb +0 -351
  86. data/spec/ajax-datatables-rails/kaminari_spec.rb +0 -35
  87. data/spec/ajax-datatables-rails/models_spec.rb +0 -10
  88. data/spec/ajax-datatables-rails/simple_paginator_spec.rb +0 -32
  89. data/spec/ajax-datatables-rails/will_paginate_spec.rb +0 -28
  90. data/spec/schema.rb +0 -35
  91. data/spec/test_models.rb +0 -21
data/README.md CHANGED
@@ -1,604 +1,656 @@
1
1
  # ajax-datatables-rails
2
2
 
3
- [![Build Status](https://travis-ci.org/antillas21/ajax-datatables-rails.svg?branch=master)](https://travis-ci.org/antillas21/ajax-datatables-rails)
4
- [![Gem Version](https://badge.fury.io/rb/ajax-datatables-rails.svg)](http://badge.fury.io/rb/ajax-datatables-rails)
5
- [![Code Climate](https://codeclimate.com/github/antillas21/ajax-datatables-rails/badges/gpa.svg)](https://codeclimate.com/github/antillas21/ajax-datatables-rails)
3
+ [![GitHub license](https://img.shields.io/github/license/jbox-web/ajax-datatables-rails.svg)](https://github.com/jbox-web/ajax-datatables-rails/blob/master/LICENSE)
4
+ [![Gem](https://img.shields.io/gem/v/ajax-datatables-rails.svg)](https://rubygems.org/gems/ajax-datatables-rails)
5
+ [![Gem](https://img.shields.io/gem/dtv/ajax-datatables-rails.svg)](https://rubygems.org/gems/ajax-datatables-rails)
6
+ [![CI](https://github.com/jbox-web/ajax-datatables-rails/workflows/CI/badge.svg)](https://github.com/jbox-web/ajax-datatables-rails/actions)
7
+ [![Code Climate](https://codeclimate.com/github/jbox-web/ajax-datatables-rails/badges/gpa.svg)](https://codeclimate.com/github/jbox-web/ajax-datatables-rails)
8
+ [![Test Coverage](https://codeclimate.com/github/jbox-web/ajax-datatables-rails/badges/coverage.svg)](https://codeclimate.com/github/jbox-web/ajax-datatables-rails/coverage)
6
9
 
7
- ### Versions
10
+ **Important : This gem is targeted at DataTables version 1.10.x.**
8
11
 
9
- [Datatables](http://datatables.net) recently released version 1.10 (which
10
- includes a new API and features) and deprecated version 1.9.
12
+ It's tested against :
11
13
 
12
- If you have dataTables 1.9 in your project and want to keep using it, please
13
- use this gem's version `0.1.x` in your `Gemfile`:
14
+ * Rails 5.2.4 / 6.0.3 / 6.1.0
15
+ * Ruby 2.5.x / 2.6.x / 2.7.x
16
+ * SQLite3
17
+ * Postgresql 13
18
+ * MySQL 8
19
+ * Oracle XE 11.2 (thanks to [travis-oracle](https://github.com/cbandy/travis-oracle))
20
+
21
+ ## Description
22
+
23
+ > [DataTables](https://datatables.net/) is a nifty jQuery plugin that adds the ability to paginate, sort, and search your html tables.
24
+ > When dealing with large tables (more than a couple of hundred rows) however, we run into performance issues.
25
+ > These can be fixed by using server-side pagination, but this breaks some DataTables functionality.
26
+ >
27
+ > `ajax-datatables-rails` is a wrapper around DataTables ajax methods that allow synchronization with server-side pagination in a Rails app.
28
+ > It was inspired by this [Railscast](http://railscasts.com/episodes/340-datatables).
29
+ > I needed to implement a similar solution in a couple projects I was working on, so I extracted a solution into a gem.
30
+ >
31
+ > Joel Quenneville (original author)
32
+ >
33
+ > I needed a good gem to manage a lot of DataTables so I chose this one :)
34
+ >
35
+ > Nicolas Rodriguez (current maintainer)
36
+
37
+ The final goal of this gem is to **generate a JSON** content that will be given to jQuery DataTables.
38
+ All the datatable customizations (header, tr, td, css classes, width, height, buttons, etc...) **must** take place in the [javascript definition](#5-wire-up-the-javascript) of the datatable.
39
+ jQuery DataTables is a very powerful tool with a lot of customizations available. Take the time to [read the doc](https://datatables.net/reference/option/).
40
+
41
+ You'll find a sample project here : https://ajax-datatables-rails.herokuapp.com
42
+
43
+ Its real world examples. The code is here : https://github.com/jbox-web/ajax-datatables-rails-sample-project
44
+
45
+
46
+ ## Installation
47
+
48
+ Add these lines to your application's Gemfile:
14
49
 
15
50
  ```ruby
16
- # specific version number
17
- gem 'ajax-datatables-rails', '0.1.2'
51
+ gem 'ajax-datatables-rails'
52
+ ```
18
53
 
19
- # or, support on datatables 1.9
20
- gem 'ajax-datatables-rails', git: 'git://github.com/antillas21/ajax-datatables-rails.git', branch: 'legacy'
54
+ And then execute:
55
+
56
+ ```sh
57
+ $ bundle install
21
58
  ```
22
59
 
23
- If you have dataTables 1.10 in your project, then use the gem's latest version,
24
- or point to the `master` branch.
60
+ We assume here that you have already installed [jQuery DataTables](https://datatables.net/).
25
61
 
62
+ You can install jQuery DataTables :
26
63
 
27
- ## Description
64
+ * with the [`jquery-datatables`](https://github.com/mkhairi/jquery-datatables) gem
65
+ * by adding the assets manually (in `vendor/assets`)
66
+ * with [Rails webpacker gem](https://github.com/rails/webpacker) (see [here](/doc/webpack.md) for more infos)
28
67
 
29
- Datatables is a nifty jquery plugin that adds the ability to paginate, sort,
30
- and search your html tables. When dealing with large tables
31
- (more than a couple hundred rows) however, we run into performance issues.
32
- These can be fixed by using server-side pagination, but this breaks some
33
- datatables functionality.
34
68
 
35
- `ajax-datatables-rails` is a wrapper around datatable's ajax methods that allow
36
- synchronization with server-side pagination in a rails app. It was inspired by
37
- this [Railscast](http://railscasts.com/episodes/340-datatables). I needed to
38
- implement a similar solution in a couple projects I was working on, so I
39
- extracted a solution into a gem.
69
+ ## Note
40
70
 
41
- ## ORM support
71
+ Currently `AjaxDatatablesRails` only supports `ActiveRecord` as ORM for performing database queries.
42
72
 
43
- Currently `AjaxDatatablesRails` only supports `ActiveRecord` as ORM for
44
- performing database queries.
73
+ Adding support for `Sequel`, `Mongoid` and `MongoMapper` is (more or less) a planned feature for this gem.
45
74
 
46
- Adding support for `Sequel`, `Mongoid` and `MongoMapper` is a planned feature
47
- for this gem. If you'd be interested in contributing to speed development,
48
- please [open an issue](https://github.com/antillas21/ajax-datatables-rails/issues/new)
49
- and get in touch.
75
+ If you'd be interested in contributing to speed development, please [open an issue](https://github.com/antillas21/ajax-datatables-rails/issues/new) and get in touch.
50
76
 
51
- ## Installation
52
77
 
53
- Add these lines to your application's Gemfile:
78
+ ## Quick start (in 5 steps)
54
79
 
55
- gem 'jquery-datatables-rails'
56
- gem 'ajax-datatables-rails'
80
+ The following examples assume that we are setting up `ajax-datatables-rails` for an index page of users from a `User` model,
81
+ and that we are using Postgresql as our db, because you **should be using it**. (It also works with other DB, [see above](#change-the-db-adapter-for-a-datatable-class))
57
82
 
58
- And then execute:
83
+ The goal is to render a users table and display : `id`, `first name`, `last name`, `email`, and `bio` for each user.
59
84
 
60
- $ bundle
85
+ Something like this:
61
86
 
62
- The `jquery-datatables-rails` gem is listed as a convenience, to ease adding
63
- jQuery dataTables to your Rails project. You can always add the plugin assets
64
- manually via the assets pipeline. If you decide to use the
65
- `jquery-datatables-rails` gem, please refer to its installation instructions
66
- [here](https://github.com/rweng/jquery-datatables-rails).
87
+ |ID |First Name|Last Name|Email |Brief Bio|
88
+ |---|----------|---------|----------------------|---------|
89
+ | 1 |John |Doe |john.doe@example.net |Is your default user everywhere|
90
+ | 2 |Jane |Doe |jane.doe@example.net |Is John's wife|
91
+ | 3 |James |Doe |james.doe@example.net |Is John's brother and best friend|
67
92
 
68
- ## Usage (0.3.x)
69
- *The following examples assume that we are setting up ajax-datatables-rails for
70
- an index of users from a `User` model, and that we are using postgresql as
71
- our db, because you __should be using it__, if not, please refer to the
72
- [Searching on non text-based columns](#searching-on-non-text-based-columns)
73
- entry in the Additional Notes section.*
93
+ Here the steps we're going through :
74
94
 
75
- ### Generate
76
- Run the following command:
95
+ 1. [Generate the datatable class](#1-generate-the-datatable-class)
96
+ 2. [Build the View](#2-build-the-view)
97
+ 3. [Customize the generated Datatables class](#3-customize-the-generated-datatables-class)
98
+ 4. [Setup the Controller action](#4-setup-the-controller-action)
99
+ 5. [Wire up the Javascript](#5-wire-up-the-javascript)
100
+
101
+ ### 1) Generate the datatable class
77
102
 
78
- $ rails generate datatable User
103
+ Run the following command:
79
104
 
105
+ ```sh
106
+ $ rails generate datatable User
107
+ ```
80
108
 
81
109
  This will generate a file named `user_datatable.rb` in `app/datatables`.
82
110
  Open the file and customize in the functions as directed by the comments.
83
111
 
84
112
  Take a look [here](#generator-syntax) for an explanation about the generator syntax.
85
113
 
86
- ### Customize
87
- ```ruby
88
- def sortable_columns
89
- # Declare strings in this format: ModelName.column_name
90
- @sortable_columns ||= []
91
- end
92
114
 
93
- def searchable_columns
94
- # Declare strings in this format: ModelName.column_name
95
- @searchable_columns ||= []
96
- end
115
+ ### 2) Build the View
116
+
117
+ You should always start by the single source of truth, which is your html view.
118
+
119
+ * Set up an html `<table>` with a `<thead>` and `<tbody>`
120
+ * Add in your table headers if desired
121
+ * Don't add any rows to the body of the table, DataTables does this automatically
122
+ * Add a data attribute to the `<table>` tag with the url of the JSON feed, in our case is the `users_path` as we're pointing to the `UsersController#index` action
123
+
124
+
125
+ ```html
126
+ <table id="users-datatable" data-source="<%= users_path(format: :json) %>">
127
+ <thead>
128
+ <tr>
129
+ <th>ID</th>
130
+ <th>First Name</th>
131
+ <th>Last Name</th>
132
+ <th>Email</th>
133
+ <th>Brief Bio</th>
134
+ </tr>
135
+ </thead>
136
+ <tbody>
137
+ </tbody>
138
+ </table>
97
139
  ```
98
140
 
99
- * For `sortable_columns`, assign an array of the database columns that
100
- correspond to the columns in our view table. For example
101
- `[users.f_name, users.l_name, users.bio]`. This array is used for sorting by
102
- various columns. The sequence of these 3 columns must mirror the order of
103
- declarations in the `data` method below. You cannot leave this array empty as of
104
- 0.3.0.
105
141
 
106
- * For `searchable_columns`, assign an array of the database columns that you
107
- want searchable by datatables. Suppose we need to sort and search users
108
- `:first_name`, `last_name` and `bio`.
142
+ ### 3) Customize the generated Datatables class
143
+
144
+ #### a. Declare columns mapping
145
+
146
+ First we need to declare in `view_columns` the list of the model(s) columns mapped to the data we need to present.
147
+ In this case: `id`, `first_name`, `last_name`, `email` and `bio`.
109
148
 
110
149
  This gives us:
111
150
 
112
151
  ```ruby
113
- include AjaxDatatablesRails::Extensions::Kaminari
152
+ def view_columns
153
+ @view_columns ||= {
154
+ id: { source: "User.id" },
155
+ first_name: { source: "User.first_name", cond: :like, searchable: true, orderable: true },
156
+ last_name: { source: "User.last_name", cond: :like, nulls_last: true },
157
+ email: { source: "User.email" },
158
+ bio: { source: "User.bio" },
159
+ }
160
+ end
161
+ ```
162
+
163
+ **Notes :** by default `orderable` and `searchable` are true and `cond` is `:like`.
164
+
165
+ `cond` can be :
166
+
167
+ * `:like`, `:start_with`, `:end_with`, `:string_eq`, `:string_in` for string or full text search
168
+ * `:eq`, `:not_eq`, `:lt`, `:gt`, `:lteq`, `:gteq`, `:in` for numeric
169
+ * `:date_range` for date range
170
+ * `:null_value` for nil field
171
+ * `Proc` for whatever (see [here](https://github.com/jbox-web/ajax-datatables-rails-sample-project/blob/master/app/datatables/city_datatable.rb) for real example)
172
+
173
+ The `nulls_last` param allows for nulls to be ordered last. You can configure it by column, like above, or by datatable class :
174
+
175
+ ```ruby
176
+ class MyDatatable < AjaxDatatablesRails::ActiveRecord
177
+ self.nulls_last = true
114
178
 
115
- def sortable_columns
116
- @sortable_columns ||= %w(User.first_name User.last_name User.bio)
117
- # this is equal to:
118
- # @sortable_columns ||= ['User.first_name', 'User.last_name', 'User.bio']
179
+ # ... other methods (view_columns, data...)
119
180
  end
181
+ ```
182
+
183
+ See [here](#columns-syntax) to get more details about columns definitions and how to play with associated models.
184
+
185
+ You can customize or sanitize the search value passed to the DB by using the `:formatter` option with a lambda :
120
186
 
121
- def searchable_columns
122
- @searchable_columns ||= %w(User.first_name User.last_name User.bio)
123
- # this is equal to:
124
- # @searchable_columns ||= ['User.first_name', 'User.last_name', 'User.bio']
187
+ ```ruby
188
+ def view_columns
189
+ @view_columns ||= {
190
+ id: { source: "User.id" },
191
+ first_name: { source: "User.first_name" },
192
+ last_name: { source: "User.last_name" },
193
+ email: { source: "User.email", formatter: -> (o) { o.upcase } },
194
+ bio: { source: "User.bio" },
195
+ }
125
196
  end
126
197
  ```
127
198
 
128
- * [See here](#searching-on-non-text-based-columns) for notes about the
129
- `searchable_columns` settings (if using something different from `postgre`).
130
- * [Read these notes](#searchable-and-sortable-columns-syntax) about
131
- considerations for the `searchable_columns` and `sortable_columns` methods.
199
+ The object passed to the lambda is the search value.
200
+
201
+ #### b. Map data
202
+
203
+ Then we need to map the records retrieved by the `get_raw_records` method to the real values we want to display :
132
204
 
133
- ### Map data
134
205
  ```ruby
135
206
  def data
136
207
  records.map do |record|
137
- [
138
- # comma separated list of the values for each cell of a table row
139
- # example: record.attribute,
140
- ]
208
+ {
209
+ id: record.id,
210
+ first_name: record.first_name,
211
+ last_name: record.last_name,
212
+ email: record.email,
213
+ bio: record.bio,
214
+ DT_RowId: record.id, # This will automagically set the id attribute on the corresponding <tr> in the datatable
215
+ }
141
216
  end
142
217
  end
143
218
  ```
144
219
 
145
- This method builds a 2D array that is used by datatables to construct the html
220
+ **Deprecated:** You can either use the v0.3 Array style for your columns :
221
+
222
+ This method builds a 2d array that is used by datatables to construct the html
146
223
  table. Insert the values you want on each column.
147
224
 
148
225
  ```ruby
149
226
  def data
150
227
  records.map do |record|
151
228
  [
229
+ record.id,
152
230
  record.first_name,
153
231
  record.last_name,
232
+ record.email,
154
233
  record.bio
155
234
  ]
156
235
  end
157
236
  end
158
237
  ```
159
238
 
160
- In the example above, we use the same sequence of column declarations as in
161
- `sortable_columns`. This ordering is important! And as of 0.3.0, the first
162
- column must be a sortable column. For more, see
163
- [this issue](https://github.com/antillas21/ajax-datatables-rails/issues/83).
164
-
165
- [See here](#using-view-helpers) if you need to use view helpers in the
166
- returned 2D array, like `link_to`, `mail_to`, `resource_path`, etc.
239
+ The drawback of this method is that you can't pass the `DT_RowId` so it's tricky to set the id attribute on the corresponding `<tr>` in the datatable (need to be done on JS side).
167
240
 
168
- #### Automatic addition of ID
169
- If you want the gem inserts automatically the ID of the record in the `<tr>` element
170
- as shown in this [DataTable axample](http://www.datatables.net/examples/server_side/ids.html),
171
- you have to perform some modifications in both `some_datatable.rb` file and in your javascript.
172
-
173
- Here is an example:
174
- ```ruby
175
- def data
176
- records.map do |record|
177
- {
178
- '0' => record.first_name,
179
- '1' => record.last_name,
180
- '2' => record.email,
181
- 'DT_RowId' => record.id
182
- }
183
- end
184
- end
185
- ```
241
+ [See here](#using-view-helpers) if you need to use view helpers like `link_to`, `mail_to`, etc...
186
242
 
187
- and in your javascript file:
188
- ```javascript
189
- $(function() {
190
- return $('#table_id').dataTable({
191
- processing: true,
192
- serverSide: true,
193
- ajax: 'ajax_url',
194
- columns: [
195
- {data: '0' },
196
- {data: '1' },
197
- {data: '2' }
198
- ]
199
- });
200
- });
201
- ```
202
-
203
- #### Get Raw Records
204
- ```ruby
205
- def get_raw_records
206
- # insert query here
207
- end
208
- ```
243
+ #### c. Get Raw Records
209
244
 
210
245
  This is where your query goes.
211
246
 
212
247
  ```ruby
213
248
  def get_raw_records
214
- # suppose we need all User records
215
- # Rails 4+
216
249
  User.all
217
- # Rails 3.x
218
- # User.scoped
219
250
  end
220
251
  ```
221
252
 
222
- Obviously, you can construct your query as required for the use case the
223
- datatable is used. Example: `User.active.with_recent_messages`.
224
-
225
- __IMPORTANT:__ Make sure to return an `ActiveRecord::Relation` object as the
226
- end product of this method. Why? Because the result from this method, will
227
- be chained (for now) to `ActiveRecord` methods for sorting, filtering
228
- and pagination.
229
-
230
- #### Associated and nested models
231
- The previous example has only one single model. But what about if you have
232
- some associated nested models and in a report you want to show fields from
233
- these tables.
253
+ Obviously, you can construct your query as required for the use case the datatable is used.
234
254
 
235
- Take an example that has an `Event, Course, Coursetype, Allocation, Teacher,
236
- Contact, Competency and CompetencyType` models. We want to have a datatables
237
- report which has the following column:
255
+ Example:
238
256
 
239
257
  ```ruby
240
- 'coursetypes.name',
241
- 'courses.name',
242
- 'events.title',
243
- 'events.event_start',
244
- 'events.event_end',
245
- 'contacts.full_name',
246
- 'competency_types.name',
247
- 'events.status'
258
+ def get_raw_records
259
+ User.active.with_recent_messages
260
+ end
248
261
  ```
249
262
 
250
- We want to sort and search on all columns of the list. The related definition
251
- would be:
263
+ You can put any logic in `get_raw_records` [based on any parameters you inject](#pass-options-to-the-datatable-class) in the `Datatable` object.
252
264
 
253
- ```ruby
265
+ **IMPORTANT :** Because the result of this method will be chained to `ActiveRecord` methods for sorting, filtering and pagination,
266
+ make sure to return an `ActiveRecord::Relation` object.
254
267
 
255
- def sortable_columns
256
- @sortable_columns ||= [
257
- 'Coursetype.name',
258
- 'Course.name',
259
- 'Event.title',
260
- 'Event.event_start',
261
- 'Event.event_end',
262
- 'Contact.last_name',
263
- 'CompetencyType.name',
264
- 'Event.status'
265
- ]
266
- end
268
+ #### d. Additional data
267
269
 
268
- def searchable_columns
269
- @searchable_columns ||= [
270
- 'Coursetype.name',
271
- 'Course.name',
272
- 'Event.title',
273
- 'Event.event_start',
274
- 'Event.event_end',
275
- 'Contact.last_name',
276
- 'CompetencyType.name',
277
- 'Event.status'
278
- ]
279
- end
270
+ You can inject other key/value pairs in the rendered JSON by defining the `#additional_data` method :
280
271
 
281
- def get_raw_records
282
- Event.joins(
283
- { course: :coursetype },
284
- { allocations: {
285
- teacher: [:contact, {competencies: :competency_type}]
286
- }
287
- }).distinct
288
- end
272
+ ```ruby
273
+ def additional_data
274
+ {
275
+ foo: 'bar'
276
+ }
277
+ end
289
278
  ```
290
279
 
291
- __Some comments for the above code:__
292
-
293
- 1. In the list we show `full_name`, but in `sortable_columns` and
294
- `searchable_columns` we use `last_name` from the `Contact` model. The reason
295
- is we can use only database columns as sort or search fields and the full_name
296
- is not a database field.
280
+ Very useful with [datatables-factory](https://github.com/jbox-web/datatables-factory) (or [yadcf](https://github.com/vedmack/yadcf)) to provide values for dropdown filters.
297
281
 
298
- 2. In the `get_raw_records` method we have quite a complex query having one to
299
- many and may to many associations using the joins ActiveRecord method.
300
- The joins will generate INNER JOIN relations in the SQL query. In this case,
301
- we do not include all event in the report if we have events which is not
302
- associated with any model record from the relation.
303
282
 
304
- 3. To have all event records in the list we should use the `.includes` method,
305
- which generate LEFT OUTER JOIN relation of the SQL query.
306
- __IMPORTANT:__ Make sure to append `.references(:related_model)` with any
307
- associated model. That forces the eager loading of all the associated models
308
- by one SQL query, and the search condition for any column works fine.
309
- Otherwise the `:recordsFiltered => filter_records(get_raw_records).count(:all)`
310
- will generate 2 SQL queries (one for the Event model, and then another for the
311
- associated tables). The `:recordsFiltered => filter_records(get_raw_records).count(:all)`
312
- will use only the first one to return from the ActiveRecord::Relation object
313
- in `get_raw_records` and you will get an error message of __Unknown column
314
- 'yourtable.yourfield' in 'where clause'__ in case the search field value
315
- is not empty.
283
+ ### 4) Setup the Controller action
316
284
 
317
- So the query using the `.includes()` method is:
318
-
319
- ```ruby
320
- def get_raw_records
321
- Event.includes(
322
- { course: :coursetype },
323
- { allocations: {
324
- teacher: [:contact, { competencies: :competency_type }]
325
- }
326
- }
327
- ).references(:course).distinct
328
- end
329
- ```
330
-
331
- For more examples of 0.3.0 syntax for complex associations (and an example of
332
- the `data` method), read
333
- [this](https://github.com/antillas21/ajax-datatables-rails/issues/77).
334
-
335
- ### Controller
336
- Set up the controller to respond to JSON
285
+ Set the controller to respond to JSON
337
286
 
338
287
  ```ruby
339
288
  def index
340
289
  respond_to do |format|
341
290
  format.html
342
- format.json { render json: UserDatatable.new(view_context) }
291
+ format.json { render json: UserDatatable.new(params) }
343
292
  end
344
293
  end
345
294
  ```
346
295
 
347
296
  Don't forget to make sure the proper route has been added to `config/routes.rb`.
348
297
 
298
+ [See here](#pass-options-to-the-datatable-class) if you need to inject params in the `UserDatatable`.
349
299
 
350
- ### View
351
-
352
- * Set up an html `<table>` with a `<thead>` and `<tbody>`
353
- * Add in your table headers if desired
354
- * Don't add any rows to the body of the table, datatables does this automatically
355
- * Add a data attribute to the `<table>` tag with the url of the JSON feed
356
-
357
- The resulting view may look like this:
300
+ **Note :** If you have more than **2** datatables in your application, don't forget to read [this](#use-http-post-method-medium).
358
301
 
359
- ```html
360
- <table id="users-table", data-source="<%= users_path(format: :json) %>">
361
- <thead>
362
- <tr>
363
- <th>First Name</th>
364
- <th>Last Name</th>
365
- <th>Brief Bio</th>
366
- </tr>
367
- </thead>
368
- <tbody>
369
- </tbody>
370
- </table>
371
- ```
302
+ ### 5) Wire up the Javascript
372
303
 
373
- ### Javascript
374
304
  Finally, the javascript to tie this all together. In the appropriate `coffee` file:
375
305
 
376
306
  ```coffeescript
377
307
  # users.coffee
378
308
 
379
309
  $ ->
380
- $('#users-table').dataTable
310
+ $('#users-datatable').dataTable
381
311
  processing: true
382
312
  serverSide: true
383
- ajax: $('#users-table').data('source')
313
+ ajax:
314
+ url: $('#users-datatable').data('source')
384
315
  pagingType: 'full_numbers'
385
- # optional, if you want full pagination controls.
316
+ columns: [
317
+ {data: 'id'}
318
+ {data: 'first_name'}
319
+ {data: 'last_name'}
320
+ {data: 'email'}
321
+ {data: 'bio'}
322
+ ]
323
+ # pagingType is optional, if you want full pagination controls.
386
324
  # Check dataTables documentation to learn more about
387
325
  # available options.
388
326
  ```
389
327
 
390
328
  or, if you're using plain javascript:
329
+
391
330
  ```javascript
392
331
  // users.js
393
332
 
394
333
  jQuery(document).ready(function() {
395
- $('#users-table').dataTable({
334
+ $('#users-datatable').dataTable({
396
335
  "processing": true,
397
336
  "serverSide": true,
398
- "ajax": $('#users-table').data('source'),
337
+ "ajax": {
338
+ "url": $('#users-datatable').data('source')
339
+ },
399
340
  "pagingType": "full_numbers",
400
- // optional, if you want full pagination controls.
341
+ "columns": [
342
+ {"data": "id"},
343
+ {"data": "first_name"},
344
+ {"data": "last_name"},
345
+ {"data": "email"},
346
+ {"data": "bio"}
347
+ ]
348
+ // pagingType is optional, if you want full pagination controls.
401
349
  // Check dataTables documentation to learn more about
402
350
  // available options.
403
351
  });
404
352
  });
405
353
  ```
406
354
 
407
- ### Additional Notes
355
+ ## Advanced usage
408
356
 
409
- #### Searchable and Sortable columns syntax
357
+ ### Using view helpers
410
358
 
411
- Starting on version `0.3.0`, we are implementing a pseudo code way of declaring
412
- the array of both `searchable_columns` and `sortable_columns` method.
359
+ Sometimes you'll need to use view helper methods like `link_to`, `mail_to`,
360
+ `edit_user_path`, `check_box_tag` and so on in the returned JSON representation returned by the [`data`](#b-map-data) method.
413
361
 
414
- Example. Suppose we have the following models: `User`, `PurchaseOrder`,
415
- `Purchase::LineItem` and we need to have several columns from those models
416
- available in our datatable to search and sort by.
362
+ To have these methods available to be used, this is the way to go:
417
363
 
418
364
  ```ruby
419
- # we use the ModelName.column_name notation to declare our columns
365
+ class UserDatatable < AjaxDatatablesRails::ActiveRecord
366
+ extend Forwardable
420
367
 
421
- def searchable_columns
422
- @searchable_columns ||= [
423
- 'User.first_name',
424
- 'User.last_name',
425
- 'PurchaseOrder.number',
426
- 'PurchaseOrder.created_at',
427
- 'Purchase::LineItem.quantity',
428
- 'Purchase::LineItem.unit_price',
429
- 'Purchase::LineItem.item_total'
430
- ]
431
- end
368
+ # either define them one-by-one
369
+ def_delegator :@view, :check_box_tag
370
+ def_delegator :@view, :link_to
371
+ def_delegator :@view, :mail_to
372
+ def_delegator :@view, :edit_user_path
432
373
 
433
- def sortable_columns
434
- @sortable_columns ||= [
435
- 'User.first_name',
436
- 'User.last_name',
437
- 'PurchaseOrder.number',
438
- 'PurchaseOrder.created_at'
439
- ]
440
- end
441
- ```
374
+ # or define them in one pass
375
+ def_delegators :@view, :check_box_tag, :link_to, :mail_to, :edit_user_path
442
376
 
443
- ##### What if the datatable itself is namespaced?
444
- Example: what if the datatable is namespaced into an `Admin` module?
377
+ # ... other methods (view_columns, get_raw_records...)
445
378
 
446
- ```ruby
447
- module Admin
448
- class PurchasesDatatable < AjaxDatatablesRails::Base
379
+ def initialize(params, opts = {})
380
+ @view = opts[:view_context]
381
+ super
449
382
  end
450
- end
451
- ```
452
383
 
453
- Taking the same models and columns, we would define it like this:
384
+ # now, you'll have these methods available to be used anywhere
385
+ def data
386
+ records.map do |record|
387
+ {
388
+ id: check_box_tag('users[]', record.id),
389
+ first_name: link_to(record.first_name, edit_user_path(record)),
390
+ last_name: record.last_name,
391
+ email: mail_to(record.email),
392
+ bio: record.bio
393
+ DT_RowId: record.id,
394
+ }
395
+ end
396
+ end
397
+ end
454
398
 
455
- ```ruby
456
- def searchable_columns
457
- @searchable_columns ||= [
458
- '::User.first_name',
459
- '::User.last_name',
460
- '::PurchaseOrder.number',
461
- '::PurchaseOrder.created_at',
462
- '::Purchase::LineItem.quantity',
463
- '::Purchase::LineItem.unit_price',
464
- '::Purchase::LineItem.item_total'
465
- ]
399
+ # and in your controller:
400
+ def index
401
+ respond_to do |format|
402
+ format.html
403
+ format.json { render json: UserDatatable.new(params, view_context: view_context) }
404
+ end
466
405
  end
467
406
  ```
468
407
 
469
- Pretty much like you would do it, if you were inside a namespaced controller.
470
-
471
- #### What if I'm using Oracle?
408
+ ### Using view decorators
472
409
 
473
- We have recently merged and released a contribution from [lutechspa](https://github.com/lutechspa) that makes this gem work with Oracle (tested in version 11g). You can [take a look at this sample repo](https://github.com/paoloripamonti/oracle-ajax-datatable) to get an idea on how to set things up.
410
+ If you want to keep things tidy in the data mapping method, you could use
411
+ [Draper](https://github.com/drapergem/draper) to define column mappings like below.
474
412
 
475
- #### Searching on non text-based columns
413
+ **Note :** This is the recommanded way as you don't need to inject the `view_context` in the Datatable object to access helpers methods.
414
+ It also helps in separating view/presentation logic from filtering logic (the only one that really matters in a datatable class).
476
415
 
477
- It always comes the time when you need to add a non-string/non-text based
478
- column to the `@searchable_columns` array, so you can perform searches against
479
- these column types (example: numeric, date, time).
416
+ Example :
480
417
 
481
- We recently added the ability to (automatically) typecast these column types
482
- and have this scenario covered. Please note however, if you are using
483
- something different from `postgresql` (with the `:pg` gem), like `oracle`,
484
- `mysql` or `sqlite`, then you need to add an initializer in your application's
485
- `config/initializers` directory.
418
+ ```ruby
419
+ class UserDatatable < AjaxDatatablesRails::ActiveRecord
420
+ ...
421
+ def data
422
+ records.map do |record|
423
+ {
424
+ id: record.decorate.check_box,
425
+ first_name: record.decorate.link_to,
426
+ last_name: record.decorate.last_name
427
+ email: record.decorate.email,
428
+ bio: record.decorate.bio
429
+ DT_RowId: record.id,
430
+ }
431
+ end
432
+ end
433
+ ...
434
+ end
486
435
 
487
- If you don't perform this step (again, if using something different from
488
- `postgresql`), your database will complain that it does not understand the
489
- default typecast used to enable such searches.
436
+ class UserDecorator < ApplicationDecorator
437
+ delegate :last_name, :bio
490
438
 
439
+ def check_box
440
+ h.check_box_tag 'users[]', object.id
441
+ end
491
442
 
492
- #### Configuration initializer
443
+ def link_to
444
+ h.link_to object.first_name, h.edit_user_path(object)
445
+ end
493
446
 
494
- You have two options to create this initializer:
447
+ def email
448
+ h.mail_to object.email
449
+ end
495
450
 
496
- * use the provided (and recommended) generator (and then just edit the file);
497
- * create the file from scratch.
451
+ # Just an example of a complex method you can add to you decorator
452
+ # To render it in a datatable just add a column 'dt_actions' in
453
+ # 'view_columns' and 'data' methods and call record.decorate.dt_actions
454
+ def dt_actions
455
+ links = []
456
+ links << h.link_to 'Edit', h.edit_user_path(object) if h.policy(object).update?
457
+ links << h.link_to 'Delete', h.user_path(object), method: :delete, remote: true if h.policy(object).destroy?
458
+ h.safe_join(links, '')
459
+ end
460
+ end
461
+ ```
498
462
 
499
- To use the generator, from the terminal execute:
463
+ ### Pass options to the datatable class
500
464
 
501
- ```
502
- $ bundle exec rails generate datatable:config
503
- ```
465
+ An `AjaxDatatablesRails::ActiveRecord` inherited class can accept an options hash at initialization. This provides room for flexibility when required.
504
466
 
505
- Doing so, will create the `config/initializers/ajax_datatables_rails.rb` file
506
- with the following content:
467
+ Example:
507
468
 
508
469
  ```ruby
509
- AjaxDatatablesRails.configure do |config|
510
- # available options for db_adapter are: :oracle, :pg, :mysql2, :sqlite3
511
- # config.db_adapter = :pg
512
-
513
- # available options for paginator are: :simple_paginator, :kaminari, :will_paginate
514
- # config.paginator = :simple_paginator
470
+ # In the controller
471
+ def index
472
+ respond_to do |format|
473
+ format.html
474
+ format.json { render json: UserDatatable.new(params, user: current_user, from: 1.month.ago) }
475
+ end
515
476
  end
516
- ```
517
477
 
518
- Uncomment the `config.db_adapter` line and set the corresponding value to your
519
- database and gem. This is all you need.
478
+ # The datatable class
479
+ class UnrespondedMessagesDatatable < AjaxDatatablesRails::ActiveRecord
520
480
 
521
- Uncomment the `config.paginator` line to set `kaminari or will_paginate` if
522
- included in your project. It defaults to `simple_paginator`, it falls back to
523
- passing `offset` and `limit` at the database level (through `ActiveRecord`
524
- of course).
481
+ # ... other methods (view_columns, data...)
525
482
 
526
- If you want to make the file from scratch, just copy the above code block into
527
- a file inside the `config/initializers` directory.
483
+ def user
484
+ @user ||= options[:user]
485
+ end
528
486
 
487
+ def from
488
+ @from ||= options[:from].beginning_of_day
489
+ end
490
+
491
+ def to
492
+ @to ||= Date.today.end_of_day
493
+ end
529
494
 
530
- #### Using view helpers
495
+ # We can now customize the get_raw_records method
496
+ # with the options we've injected
497
+ def get_raw_records
498
+ user.messages.unresponded.where(received_at: from..to)
499
+ end
531
500
 
532
- Sometimes you'll need to use view helper methods like `link_to`, `h`, `mailto`,
533
- `edit_resource_path` in the returned JSON representation returned by the `data`
534
- method.
501
+ end
502
+ ```
535
503
 
536
- To have these methods available to be used, this is the way to go:
504
+ ### Change the DB adapter for a datatable class
505
+
506
+ If you have models from different databases you can set the `db_adapter` on the datatable class :
537
507
 
538
508
  ```ruby
539
- class MyCustomDatatable < AjaxDatatablesRails::Base
540
- # either define them one-by-one
541
- def_delegator :@view, :link_to
542
- def_delegator :@view, :h
543
- def_delegator :@view, :mail_to
509
+ class MySharedModelDatatable < AjaxDatatablesRails::ActiveRecord
510
+ self.db_adapter = :oracle_enhanced
544
511
 
545
- # or define them in one pass
546
- def_delegators :@view, :link_to, :h, :mailto, :edit_resource_path, :other_method
512
+ # ... other methods (view_columns, data...)
547
513
 
548
- # now, you'll have these methods available to be used anywhere
549
- # example: mapping the 2d jsonified array returned.
550
- def data
551
- records.map do |record|
552
- [
553
- link_to(record.fname, edit_resource_path(record)),
554
- mail_to(record.email),
555
- # other attributes
556
- ]
514
+ def get_raw_records
515
+ AnimalsRecord.connected_to(role: :reading) do
516
+ Dog.all
557
517
  end
558
518
  end
559
519
  end
560
520
  ```
561
521
 
562
- #### Options
522
+ ### Columns syntax
523
+
524
+ You can mix several model in the same datatable.
563
525
 
564
- An `AjaxDatatablesRails::Base` inherited class can accept an options hash at
565
- initialization. This provides room for flexibility when required. Example:
526
+ Suppose we have the following models: `User`, `PurchaseOrder`,
527
+ `Purchase::LineItem` and we need to have several columns from those models
528
+ available in our datatable to search and sort by.
566
529
 
567
530
  ```ruby
568
- class UnrespondedMessagesDatatable < AjaxDatatablesRails::Base
569
- # customized methods here
531
+ # we use the ModelName.column_name notation to declare our columns
532
+
533
+ def view_columns
534
+ @view_columns ||= {
535
+ first_name: { source: 'User.first_name' },
536
+ last_name: { source: 'User.last_name' },
537
+ order_number: { source: 'PurchaseOrder.number' },
538
+ order_created_at: { source: 'PurchaseOrder.created_at' },
539
+ quantity: { source: 'Purchase::LineItem.quantity' },
540
+ unit_price: { source: 'Purchase::LineItem.unit_price' },
541
+ item_total: { source: 'Purchase::LineItem.item_total }'
542
+ }
570
543
  end
544
+ ```
545
+
546
+ ### Associated and nested models
547
+
548
+ The previous example has only one single model. But what about if you have
549
+ some associated nested models and in a report you want to show fields from
550
+ these tables.
571
551
 
572
- datatable = UnrespondedMessagesDatatable.new(view_context,
573
- { :foo => { :bar => Baz.new }, :from => 1.month.ago }
574
- )
552
+ Take an example that has an `Event, Course, CourseType, Allocation, Teacher,
553
+ Contact, Competency and CompetencyType` models. We want to have a datatables
554
+ report which has the following column:
555
+
556
+ ```ruby
557
+ 'course_types.name'
558
+ 'courses.name'
559
+ 'contacts.full_name'
560
+ 'competency_types.name'
561
+ 'events.title'
562
+ 'events.event_start'
563
+ 'events.event_end'
564
+ 'events.status'
575
565
  ```
576
- So, now inside your class code, you can use those options like this:
577
566
 
567
+ We want to sort and search on all columns of the list.
568
+ The related definition would be :
578
569
 
579
570
  ```ruby
580
- # let's see an example
581
- def from
582
- @from ||= options[:from].beginning_of_day
571
+ def view_columns
572
+ @view_columns ||= {
573
+ course_type: { source: 'CourseType.name' },
574
+ course_name: { source: 'Course.name' },
575
+ contact_name: { source: 'Contact.full_name' },
576
+ competency_type: { source: 'CompetencyType.name' },
577
+ event_title: { source: 'Event.title' },
578
+ event_start: { source: 'Event.event_start' },
579
+ event_end: { source: 'Event.event_end' },
580
+ event_status: { source: 'Event.status' },
581
+ }
583
582
  end
584
583
 
585
- def to
586
- @to ||= Date.today.end_of_day
584
+ def get_raw_records
585
+ Event.joins(
586
+ { course: :course_type },
587
+ { allocations: {
588
+ teacher: [:contact, { competencies: :competency_type }]
589
+ }
590
+ }).distinct
587
591
  end
592
+ ```
593
+
594
+ **Some comments for the above code :**
595
+
596
+ 1. In the `get_raw_records` method we have quite a complex query having one to
597
+ many and many to many associations using the joins ActiveRecord method.
598
+ The joins will generate INNER JOIN relations in the SQL query. In this case,
599
+ we do not include all event in the report if we have events which is not
600
+ associated with any model record from the relation.
601
+
602
+ 2. To have all event records in the list we should use the `.includes` method,
603
+ which generate LEFT OUTER JOIN relation of the SQL query.
604
+
605
+ **IMPORTANT :**
588
606
 
607
+ Make sure to append `.references(:related_model)` with any
608
+ associated model. That forces the eager loading of all the associated models
609
+ by one SQL query, and the search condition for any column works fine.
610
+ Otherwise the `:recordsFiltered => filter_records(get_raw_records).count(:all)`
611
+ will generate 2 SQL queries (one for the Event model, and then another for the
612
+ associated tables). The `:recordsFiltered => filter_records(get_raw_records).count(:all)`
613
+ will use only the first one to return from the ActiveRecord::Relation object
614
+ in `get_raw_records` and you will get an error message of **Unknown column
615
+ 'yourtable.yourfield' in 'where clause'** in case the search field value
616
+ is not empty.
617
+
618
+ So the query using the `.includes()` method is:
619
+
620
+ ```ruby
589
621
  def get_raw_records
590
- Message.unresponded.where(received_at: from..to)
622
+ Event.includes(
623
+ { course: :course_type },
624
+ { allocations: {
625
+ teacher: [:contact, { competencies: :competency_type }]
626
+ }
627
+ }).references(:course).distinct
591
628
  end
592
629
  ```
593
630
 
594
- #### Generator Syntax
631
+ ### Default scope
632
+
633
+ See [DefaultScope is evil](https://rails-bestpractices.com/posts/2013/06/15/default_scope-is-evil/) and [#223](https://github.com/jbox-web/ajax-datatables-rails/issues/223) and [#233](https://github.com/jbox-web/ajax-datatables-rails/issues/233).
634
+
635
+ ### DateRange search
636
+
637
+ This feature works with [datatables-factory](https://github.com/jbox-web/datatables-factory) (or [yadcf](https://github.com/vedmack/yadcf)).
595
638
 
596
- Also, a class that inherits from `AjaxDatatablesRails::Base` is not tied to an
639
+ To enable the date range search, for example `created_at` :
640
+
641
+ * add a `created_at` `<th>` in your html
642
+ * declare your column in `view_columns` : `created_at: { source: 'Post.created_at', cond: :date_range, delimiter: '-yadcf_delim-' }`
643
+ * add it in `data` : `created_at: record.decorate.created_at`
644
+ * setup yadcf to make `created_at` search field a range
645
+
646
+ ### Generator Syntax
647
+
648
+ Also, a class that inherits from `AjaxDatatablesRails::ActiveRecord` is not tied to an
597
649
  existing model, module, constant or any type of class in your Rails app.
598
650
  You can pass a name to your datatable class like this:
599
651
 
600
652
 
601
- ```
653
+ ```sh
602
654
  $ rails generate datatable users
603
655
  # returns a users_datatable.rb file with a UsersDatatable class
604
656
 
@@ -609,21 +661,160 @@ $ rails generate datatable UnrespondedMessages
609
661
  # returns an unresponded_messages_datatable.rb file with an UnrespondedMessagesDatatable class
610
662
  ```
611
663
 
612
-
613
664
  In the end, it's up to the developer which model(s), scope(s), relationship(s)
614
665
  (or else) to employ inside the datatable class to retrieve records from the
615
666
  database.
616
667
 
617
- ## Tutorial
668
+ ## Tests
669
+
670
+ Datatables can be tested with Capybara provided you don't use Webrick during integration tests.
671
+
672
+ Long story short and as a rule of thumb : use the same webserver everywhere (dev, prod, staging, test, etc...).
673
+
674
+ If you use Puma (the Rails default webserver), use Puma everywhere, even in CI/test environment. The same goes for Thin.
675
+
676
+ You will avoid the usual story : it works in dev but not in test environment...
677
+
678
+ If you want to test datatables with a lot of data you might need this kind of tricks : https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara. (thanks CharlieIGG)
679
+
680
+ ## ProTips™
681
+
682
+ ### Create a master parent class (Easy)
683
+
684
+ In the same spirit of Rails `ApplicationController` and `ApplicationRecord`, you can create an `ApplicationDatatable` class (in `app/datatables/application_datatable.rb`)
685
+ that will be inherited from other classes :
686
+
687
+ ```ruby
688
+ class ApplicationDatatable < AjaxDatatablesRails::ActiveRecord
689
+ # puts commonly used methods here
690
+ end
691
+
692
+ class PostDatatable < ApplicationDatatable
693
+ end
694
+ ```
695
+
696
+ This way it will be easier to DRY you datatables.
697
+
698
+ ### Speedup JSON rendering (Easy)
699
+
700
+ Install [yajl-ruby](https://github.com/brianmario/yajl-ruby), basically :
701
+
702
+ ```ruby
703
+ gem 'yajl-ruby', require: 'yajl'
704
+ ```
705
+
706
+ then
707
+
708
+ ```sh
709
+ $ bundle install
710
+ ```
711
+
712
+ That's all :) ([Automatically prefer Yajl or JSON backend over Yaml, if available](https://github.com/rails/rails/commit/63bb955a99eb46e257655c93dd64e86ebbf05651))
618
713
 
619
- Tutorial for Integrating `ajax-datatable-rails`, on Rails 4 .
714
+ ### Use HTTP `POST` method (Medium)
620
715
 
621
- [Part-1 The-Installation](https://github.com/antillas21/ajax-datatables-rails/wiki/Part-1----The-Installation)
716
+ Use HTTP `POST` method to avoid `414 Request-URI Too Large` error. See : [#278](https://github.com/jbox-web/ajax-datatables-rails/issues/278) and [#308](https://github.com/jbox-web/ajax-datatables-rails/issues/308#issuecomment-424897335).
717
+
718
+ You can easily define a route concern in `config/routes.rb` and reuse it when you need it :
719
+
720
+ ```ruby
721
+ Rails.application.routes.draw do
722
+ concern :with_datatable do
723
+ post 'datatable', on: :collection
724
+ end
725
+
726
+ resources :posts, concerns: [:with_datatable]
727
+ resources :users, concerns: [:with_datatable]
728
+ end
729
+ ```
730
+
731
+ then in your controllers :
732
+
733
+ ```ruby
734
+ # PostsController
735
+ def index
736
+ end
737
+
738
+ def datatable
739
+ render json: PostDatatable.new(params)
740
+ end
741
+
742
+ # UsersController
743
+ def index
744
+ end
745
+
746
+ def datatable
747
+ render json: UserDatatable.new(params)
748
+ end
749
+ ```
750
+
751
+ then in your views :
752
+
753
+ ```html
754
+ # posts/index.html.erb
755
+ <table id="posts-datatable" data-source="<%= datatable_posts_path(format: :json) %>">
756
+
757
+ # users/index.html.erb
758
+ <table id="users-datatable" data-source="<%= datatable_users_path(format: :json) %>">
759
+ ```
760
+
761
+ then in your Coffee/JS :
762
+
763
+ ```coffee
764
+ # send params in form data
765
+ $ ->
766
+ $('#posts-datatable').dataTable
767
+ ajax:
768
+ url: $('#posts-datatable').data('source')
769
+ type: 'POST'
770
+ # ...others options, see [here](#5-wire-up-the-javascript)
771
+
772
+ # send params as json data
773
+ $ ->
774
+ $('#users-datatable').dataTable
775
+ ajax:
776
+ url: $('#users-datatable').data('source')
777
+ contentType: 'application/json'
778
+ type: 'POST'
779
+ data: (d) ->
780
+ JSON.stringify d
781
+ # ...others options, see [here](#5-wire-up-the-javascript)
782
+ ```
783
+
784
+ ### Create indices for Postgresql (Expert)
785
+
786
+ In order to speed up the `ILIKE` queries that are executed when using the default configuration, you might want to consider adding some indices.
787
+ For postgresql, you are advised to use the [gin/gist index type](http://www.postgresql.org/docs/current/interactive/pgtrgm.html).
788
+ This makes it necessary to enable the postgrsql extension `pg_trgm`. Double check that you have this extension installed before trying to enable it.
789
+ A migration for enabling the extension and creating the indices could look like this:
790
+
791
+ ```ruby
792
+ def change
793
+ enable_extension :pg_trgm
794
+ TEXT_SEARCH_ATTRIBUTES = ['your', 'attributes']
795
+ TABLE = 'your_table'
796
+
797
+ TEXT_SEARCH_ATTRIBUTES.each do |attr|
798
+ reversible do |dir|
799
+ dir.up do
800
+ execute "CREATE INDEX #{TABLE}_#{attr}_gin ON #{TABLE} USING gin(#{attr} gin_trgm_ops)"
801
+ end
802
+
803
+ dir.down do
804
+ remove_index TABLE.to_sym, name: "#{TABLE}_#{attr}_gin"
805
+ end
806
+ end
807
+ end
808
+ end
809
+ ```
810
+
811
+ ## Tutorial
622
812
 
623
- [Part 2 The Datatables with ajax functionality](https://github.com/antillas21/ajax-datatables-rails/wiki/Part-2-The-Datatables-with-ajax-functionality)
813
+ Filtering by JSONB column values : [#277](https://github.com/jbox-web/ajax-datatables-rails/issues/277#issuecomment-366526373)
624
814
 
625
- The complete project code for this tutorial series is available on [github](https://github.com/trkrameshkumar/simple_app).
815
+ Use [has_scope](https://github.com/plataformatec/has_scope) gem with `ajax-datatables-rails` : [#280](https://github.com/jbox-web/ajax-datatables-rails/issues/280)
626
816
 
817
+ Use [Datatable orthogonal data](https://datatables.net/manual/data/orthogonal-data) : see [#269](https://github.com/jbox-web/ajax-datatables-rails/issues/269#issuecomment-387940478)
627
818
 
628
819
  ## Contributing
629
820