ajax-datatables-rails 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 07fee37ffeee9afc526dfe9f6e3bc348fa7a0025
4
+ data.tar.gz: 775fc632420c285f59e0f44e7ccb09d3b6b9e63d
5
+ SHA512:
6
+ metadata.gz: c3a210c5d90332b96b23a36ce135254c382929332fb7cc0a84b0097aadcd5504d55b56cbd37d28c829cd13c8670da35c5d497635495e2c708afa4ad8b52de322
7
+ data.tar.gz: 7c31ff0b9f69466d788a68db525c13fe208ef59c06e16b13f06abe5de32d5484a3e0d94fbe8534d09932e759a8f4d0d77b035b6e78206cd827ff1c32a0a8ec34
@@ -0,0 +1,17 @@
1
+ # CHANGELOG
2
+
3
+ ## 0.1.0
4
+ * A fresh start. Sets base class name to: `AjaxDatatablesRails::Base`.
5
+ * Extracts pagination functions to mixable modules.
6
+ * A user would have the option to stick to the base
7
+ `AjaxDatatablesRails::Extensions::SimplePaginator` or replace it with
8
+ `AjaxDatatablesRails::Extensions::Kaminari` or
9
+ `AjaxDatatablesRails::Extensions::WillPaginate`, depending on what he/she is using to handle record pagination.
10
+ * Removes dependency to pass in a model name to the generator. This way,
11
+ the developer has more flexibility to implement whatever datatable feature is required.
12
+ * Datatable constructor accepts an optional `options` hash to provide
13
+ more flexibility.
14
+ See [README](https://github.com/antillas21/ajax-datatables-rails/blob/master/README.mds#options) for examples.
15
+ * Sets generator inside the `Rails` namespace. To generate an
16
+ `AjaxDatatablesRails` child class, just execute the
17
+ generator like this: `$ rails generate datatable NAME`.
data/README.md CHANGED
@@ -1,60 +1,105 @@
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
+
6
+ ### Under new management
7
+
8
+ > Hi,
9
+ >
10
+ > New maintainer here. Just to let you know that we have
11
+ > released version 0.1.0 which includes breaking changes.
12
+ >
13
+ > Please check the [CHANGELOG](https://github.com/antillas21/ajax-datatables-rails/blob/master/CHANGELOG.md) to learn about these changes.
14
+ >
15
+ > All changes have been documented below.
16
+
17
+
3
18
  Datatables is a nifty jquery plugin that adds the ability to paginate, sort, and search your html tables. When dealing with large tables (more than a couple hundred rows) however, we run into performance issues. These can be fixed by using server-side pagination, but this breaks some datatables functionality.
4
19
 
5
- `ajax-datatables-rails` is a wrapper around datatable's ajax methods that allow synchronization with server-side pagination in a rails app.
20
+ `ajax-datatables-rails` is a wrapper around datatable's ajax methods that allow synchronization with server-side pagination in a rails app. It was inspired by this [Railscast](http://railscasts.com/episodes/340-datatables). I needed to implement a similar solution in a couple projects I was working on so I extracted it out into a gem.
21
+
22
+ ## ORM support
23
+
24
+ Currently `AjaxDatatablesRails` only supports `ActiveRecord` as ORM for performing database queries.
25
+
26
+ Adding support for `Sequel`, `Mongoid` and `MongoMapper` is a planned feature for this gem. 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.
6
27
 
7
28
  ## Installation
8
29
 
9
30
  Add these lines to your application's Gemfile:
10
31
 
11
32
  gem 'jquery-datatables-rails'
12
- gem 'ajax-datatables-rails'
33
+ gem 'rails-datatables'
13
34
 
14
35
  And then execute:
15
36
 
16
37
  $ bundle
17
38
 
18
39
  ## Usage
19
- *The following examples assume that we are setting up ajax-datatables-rails for an index of users from a `User` model*
20
- ### Model
40
+ *The following examples assume that we are setting up rails-datatables for an index of users from a `User` model*
41
+
42
+ ### Generate
21
43
  Run the following command:
22
44
 
23
- $ rails generate ajaxdatatable User
45
+ $ rails generate datatable User
46
+
24
47
 
25
- This will generate a file named `users_datatable.rb` in `app/datatables`. Open the file and customize in the functions as directed by the comments
48
+ This will generate a file named `user_datatable.rb` in `app/datatables`. Open the file and customize in the functions as directed by the comments.
26
49
 
27
- #### Initializer
50
+ Take a look [here](#generator-syntax) for an explanation about the generator syntax.
51
+
52
+ ### Customize
28
53
  ```ruby
29
- def initialize(view)
30
- @model_name = User
31
- @columns = # insert array of column names here
32
- @searchable_columns = #insert array of columns that will be searched
33
- super(view)
54
+ # uncomment the appropriate paginator module,
55
+ # depending on gems available in your project.
56
+ # include AjaxDatatablesRails::Extensions::Kaminari
57
+ # include AjaxDatatablesRails::Extensions::WillPaginate
58
+ # include AjaxDatatablesRails::Extensions::SimplePaginator
59
+
60
+ def sortable_columns
61
+ # list columns inside the Array in string dot notation.
62
+ # Example: 'users.email'
63
+ @sortable_columns ||= []
64
+ end
65
+
66
+ def searchable_columns
67
+ # list columns inside the Array in string dot notation.
68
+ # Example: 'users.email'
69
+ @searchable_columns ||= []
34
70
  end
35
71
  ```
36
72
 
37
- * For `@columns`, assign an array of the database columns that correspond to the columns in our view table. For example `[users.f_name, users.l_name, users.bio]`. This array is used for sorting by various columns
73
+ * For `paginator options`, just uncomment the paginator you would like to use, given
74
+ the gems bundled in your project. For example, if your models are using `Kaminari`, uncomment `AjaxDatatablesRails::Extensions::Kaminari`. You may remove all commented lines.
75
+ * `SimplePaginator` is the most basic of them all, it falls back to passing `offset` and `limit` at the database level (through `ActiveRecord` of course, as that is the only ORM supported for the time being).
76
+
77
+ * For `sortable_columns`, assign an array of the database columns that correspond to the columns in our view table. For example `[users.f_name, users.l_name, users.bio]`. This array is used for sorting by various columns.
38
78
 
39
- * For `@searchable_columns`, assign an array of the database columns that you want searchable by datatables. For example `[users.f_name, users.l_name]`
79
+ * For `searchable_columns`, assign an array of the database columns that you want searchable by datatables. For example `[users.f_name, users.l_name]`
40
80
 
41
- This gives us:
81
+ This gives us:
42
82
  ```ruby
43
- def initialize(view)
44
- @model_name = User
45
- @columns = [users.f_name, users.l_name, users.bio]
46
- @searchable_columns = [users.f_name, users.l_name]
47
- super(view)
83
+ include AjaxDatatablesRails::Extensions::Kaminari
84
+
85
+ def sortable_columns
86
+ @sortable_columns ||= ['users.f_name', 'users.l_name', 'users.bio']
87
+ end
88
+
89
+ def searchable_columns
90
+ @searchable_columns ||= ['users.f_name', 'users.l_name']
48
91
  end
92
+
49
93
  ```
50
94
 
51
- #### Data
95
+ ### Map data
52
96
  ```ruby
53
97
  def data
54
- users.map do |user|
98
+ records.map do |record|
55
99
  [
56
- # comma separated list of the values for each cell of a table row
57
- ]
100
+ # comma separated list of the values for each cell of a table row
101
+ # example: record.attribute,
102
+ ]
58
103
  end
59
104
  end
60
105
  ```
@@ -63,11 +108,11 @@ This method builds a 2d array that is used by datatables to construct the html t
63
108
 
64
109
  ```ruby
65
110
  def data
66
- users.map do |user|
111
+ records.map do |record|
67
112
  [
68
- user.f_name,
69
- user.l_name,
70
- user.bio
113
+ record.f_name,
114
+ record.l_name,
115
+ record.bio
71
116
  ]
72
117
  end
73
118
  end
@@ -88,6 +133,8 @@ def get_raw_records
88
133
  end
89
134
  ```
90
135
 
136
+ Obviously, you can construct your query as required for the use case the datatable is used. Example: `User.active.with_recent_messages`.
137
+
91
138
  ### Controller
92
139
  Set up the controller to respond to JSON
93
140
 
@@ -95,11 +142,13 @@ Set up the controller to respond to JSON
95
142
  def index
96
143
  respond_to do |format|
97
144
  format.html
98
- format.json { render json: UsersDatatable.new(view_context) }
145
+ format.json { render json: UserDatatable.new(view_context) }
99
146
  end
100
147
  end
101
148
  ```
102
149
 
150
+ Don't forget to make sure the proper route has been added to `config/routes.rb`.
151
+
103
152
  ### View
104
153
  * Set up an html `<table>` with a `<thead>` and `<tbody>`
105
154
  * Add in your table headers if desired
@@ -109,7 +158,7 @@ end
109
158
  The resulting view may look like this:
110
159
 
111
160
  ```erb
112
- <table id="user-table", data-source="<%= users_path(format: :json) %>">
161
+ <table id="users-table", data-source="<%= users_path(format: :json) %>">
113
162
  <thead>
114
163
  <tr>
115
164
  <th>First Name</th>
@@ -131,8 +180,68 @@ $ ->
131
180
  bProcessing: true
132
181
  bServerSide: true
133
182
  sAjaxSource: $('#users-table').data('source')
183
+ sPaginationType: 'full_numbers'
184
+ # optional, if you want full pagination controls.
185
+ # Check dataTables documentation to learn more about
186
+ # available options.
187
+ ```
188
+
189
+ or, if you're using plain javascript:
190
+ ```javascript
191
+ // users.js
192
+
193
+ jQuery(document).ready(function() {
194
+ $('#users-table').dataTable({
195
+ 'bProcessing': true,
196
+ 'bServerSide': true,
197
+ 'sAjaxSource': $('#users-table').data('source'),
198
+ 'sPaginationType': 'full_numbers',
199
+ // optional, if you want full pagination controls.
200
+ // Check dataTables documentation to learn more about
201
+ // available options.
202
+ });
203
+ });
134
204
  ```
135
205
 
206
+ ### Additional Notes
207
+
208
+ #### Options
209
+
210
+ An `AjaxDatatablesRails::Base` inherited class can accept an options hash at initialization. This provides room for flexibility when required. Example:
211
+
212
+ ```ruby
213
+ class UnrespondedMessagesDatatable < AjaxDatatablesRails::Base
214
+ # customized methods here
215
+ end
216
+
217
+ datatable = UnrespondedMessagesDatatable.new(view_context,
218
+ { :foo => { :bar => Baz.new }, :from => 1.month.ago }
219
+ )
220
+
221
+ datatable.options
222
+ #=> { :foo => { :bar => #<Baz:0x007fe9cb4e0220> }, :from => 2014-04-16 19:55:28 -0700 }
223
+ ```
224
+
225
+ #### Generator Syntax
226
+
227
+ Also, a class that inherits from `AjaxDatatablesRails::Base` is not tied to an existing model, module, constant or any type of class in your Rails app. You can pass a name to your datatable class like this:
228
+
229
+
230
+ ```
231
+ $ rails generate datatable users
232
+ # returns a users_datatable.rb file with a UsersDatatable class
233
+
234
+ $ rails generate datatable contact_messages
235
+ # returns a contact_messages_datatable.rb file with a ContactMessagesDatatable class
236
+
237
+ $ rails generate datatable UnrespondedMessages
238
+ # returns an unresponded_messages_datatable.rb file with an UnrespondedMessagesDatatable class
239
+ ```
240
+
241
+
242
+ In the end, it's up to the developer which model(s), scope(s), relationship(s) (or else) to employ inside the datatable class to retrieve records from the database.
243
+
244
+
136
245
  ## Contributing
137
246
 
138
247
  1. Fork it
data/Rakefile CHANGED
@@ -1,6 +1,14 @@
1
1
  #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
2
+ require 'bundler/gem_tasks'
3
3
  require 'rspec/core/rake_task'
4
4
 
5
5
  RSpec::Core::RakeTask.new(:spec)
6
- task default: :spec
6
+ task :default => :spec
7
+
8
+ task :console do
9
+ require 'pry'
10
+ require 'rails'
11
+ require 'ajax-datatables-rails'
12
+ ARGV.clear
13
+ Pry.start
14
+ end
@@ -1,79 +1,8 @@
1
- # require 'rails'
2
-
3
- class AjaxDatatablesRails
4
-
5
- class MethodError < StandardError; end
6
-
7
- VERSION = '0.0.1'
8
-
9
- attr_reader :columns, :model_name, :searchable_columns
10
-
11
- def initialize(view)
12
- @view = view
13
- end
14
-
15
- def method_missing(meth, *args, &block)
16
- @view.send(meth, *args, &block)
17
- end
18
-
19
- def as_json(options = {})
20
- {
21
- sEcho: params[:sEcho].to_i,
22
- iTotalRecords: @model_name.count,
23
- iTotalDisplayRecords: filtered_record_count,
24
- aaData: data
25
- }
26
- end
27
-
28
- private
29
-
30
- def data
31
- raise MethodError, "The method `data' is not defined."
32
- end
33
-
34
- def get_raw_records
35
- raise MethodError, "The method `get_raw_records' is not defined."
36
- end
37
-
38
- def filtered_record_count
39
- search_records(get_raw_records).count
40
- end
41
-
42
- def fetch_records
43
- search_records(sort_records(paginate_records(get_raw_records)))
44
- end
45
-
46
- def paginate_records(records)
47
- records.offset((page - 1) * per_page).limit(per_page)
48
- end
49
-
50
- def sort_records(records)
51
- records.order("#{sort_column} #{sort_direction}")
52
- end
53
-
54
- def search_records(records)
55
- if params[:sSearch].present?
56
- query = @searchable_columns.map do |column|
57
- "#{column} LIKE :search"
58
- end.join(" OR ")
59
- records = records.where(query, search: "%#{params[:sSearch]}%")
60
- end
61
- return records
62
- end
63
-
64
- def page
65
- params[:iDisplayStart].to_i/per_page + 1
66
- end
67
-
68
- def per_page
69
- params[:iDisplayLength].to_i > 0 ? params[:iDisplayLength].to_i : 10
70
- end
71
-
72
- def sort_column
73
- @columns[params[:iSortCol_0].to_i]
74
- end
75
-
76
- def sort_direction
77
- params[:sSortDir_0] == "desc" ? "DESC" : "ASC"
78
- end
79
- end
1
+ require 'ajax-datatables-rails/version'
2
+ require 'ajax-datatables-rails/base'
3
+ require 'ajax-datatables-rails/extensions/simple_paginator'
4
+ require 'ajax-datatables-rails/extensions/kaminari'
5
+ require 'ajax-datatables-rails/extensions/will_paginate'
6
+
7
+ module AjaxDatatablesRails
8
+ end
@@ -0,0 +1,128 @@
1
+ module AjaxDatatablesRails
2
+ class Base
3
+ extend Forwardable
4
+ class MethodNotImplementedError < StandardError; end
5
+
6
+ attr_reader :view, :options, :sortable_columns, :searchable_columns
7
+ def_delegator :@view, :params, :params
8
+
9
+ def initialize(view, options = {})
10
+ @view = view
11
+ @options = options
12
+ end
13
+
14
+ def sortable_columns
15
+ @sortable_columns ||= []
16
+ end
17
+
18
+ def searchable_columns
19
+ @searchable_columns ||= []
20
+ end
21
+
22
+ def data
23
+ fail(
24
+ MethodNotImplementedError,
25
+ 'Please implement this method in your class.'
26
+ )
27
+ end
28
+
29
+ def get_raw_records
30
+ fail(
31
+ MethodNotImplementedError,
32
+ 'Please implement this method in your class.'
33
+ )
34
+ end
35
+
36
+ def as_json(options = {})
37
+ {
38
+ :sEcho => params[:sEcho].to_i,
39
+ :iTotalRecords => get_raw_records.count,
40
+ :iTotalDisplayRecords => filter_records(get_raw_records).count,
41
+ :aaData => data
42
+ }
43
+ end
44
+
45
+ private
46
+
47
+ def records
48
+ @records ||= fetch_records
49
+ end
50
+
51
+ def fetch_records
52
+ records = get_raw_records
53
+ records = sort_records(records)
54
+ records = filter_records(records)
55
+ records = paginate_records(records)
56
+ records
57
+ end
58
+
59
+ def sort_records(records)
60
+ records.order("#{sort_column} #{sort_direction}")
61
+ end
62
+
63
+ def paginate_records(records)
64
+ fail(
65
+ MethodNotImplementedError,
66
+ 'Please mixin a pagination extension.'
67
+ )
68
+ end
69
+
70
+ def filter_records(records)
71
+ records = simple_search(records)
72
+ records = composite_search(records)
73
+ records
74
+ end
75
+
76
+ def simple_search(records)
77
+ return records unless params[:sSearch]
78
+ conditions = build_conditions_for(params[:sSearch])
79
+ records = records.where(conditions) if conditions
80
+ records
81
+ end
82
+
83
+ def composite_search(records)
84
+ conditions = aggregate_query
85
+ records = records.where(conditions) if conditions
86
+ records
87
+ end
88
+
89
+ def build_conditions_for(query)
90
+ searchable_columns.map { |col| search_condition(col, query) }.reduce(:or)
91
+ end
92
+
93
+ def search_condition(column, value)
94
+ model, column = column.split('.')
95
+ model = model.singularize.titleize.constantize
96
+ model.arel_table[column.to_sym].matches("%#{value}%")
97
+ end
98
+
99
+ def aggregate_query
100
+ conditions = searchable_columns.each_with_index.map do |column, index|
101
+ value = params["sSearch_#{index}".to_sym]
102
+ search_condition(column, value) if value
103
+ end
104
+ conditions.compact.reduce(:and)
105
+ end
106
+
107
+ def offset
108
+ (page - 1) * per_page
109
+ end
110
+
111
+ def page
112
+ (params[:iDisplayStart].to_i / per_page) + 1
113
+ end
114
+
115
+ def per_page
116
+ params.fetch(:iDisplayLength, 10).to_i
117
+ end
118
+
119
+ def sort_column
120
+ sortable_columns[params[:iSortCol_0].to_i]
121
+ end
122
+
123
+ def sort_direction
124
+ options = %w(desc asc)
125
+ options.include?(params[:sSortDir_0]) ? params[:sSortDir_0].upcase : 'ASC'
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,12 @@
1
+ module AjaxDatatablesRails
2
+ module Extensions
3
+ module Kaminari
4
+
5
+ private
6
+
7
+ def paginate_records(records)
8
+ records.page(page).per(per_page)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module AjaxDatatablesRails
2
+ module Extensions
3
+ module SimplePaginator
4
+
5
+ private
6
+
7
+ def paginate_records(records)
8
+ records.offset(offset).limit(per_page)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module AjaxDatatablesRails
2
+ module Extensions
3
+ module WillPaginate
4
+
5
+ private
6
+
7
+ def paginate_records(records)
8
+ records.paginate(:page => page, :per_page => per_page)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module AjaxDatatablesRails
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,39 @@
1
+ require 'rails/generators'
2
+
3
+ module Rails
4
+ module Generators
5
+ class DatatableGenerator < ::Rails::Generators::Base
6
+ desc 'Creates a *_datatable model in the app/datatables directory.'
7
+ source_root File.expand_path('../templates', __FILE__)
8
+ argument :name, :type => :string
9
+
10
+ def generate_datatable
11
+ file_prefix = set_filename(name)
12
+ @datatable_name = set_datatable_name(name)
13
+ template 'datatable.rb', File.join(
14
+ 'app/datatables', "#{file_prefix}_datatable.rb"
15
+ )
16
+ end
17
+
18
+ private
19
+
20
+ def set_filename(name)
21
+ name.include?('_') ? name : name.to_s.underscore
22
+ end
23
+
24
+ def set_datatable_name(name)
25
+ name.include?('_') ? build_name(name) : capitalize(name)
26
+ end
27
+
28
+ def build_name(name)
29
+ pieces = name.split('_')
30
+ pieces.map(&:titleize).join
31
+ end
32
+
33
+ def capitalize(name)
34
+ return name if name[0] == name[0].upcase
35
+ name.capitalize
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ class <%= @datatable_name %>Datatable < AjaxDatatablesRails::Base
2
+ # uncomment the appropriate paginator module,
3
+ # depending on gems available in your project.
4
+ # include AjaxDatatablesRails::Extensions::Kaminari
5
+ # include AjaxDatatablesRails::Extensions::WillPaginate
6
+ # include AjaxDatatablesRails::Extensions::SimplePaginator
7
+
8
+ def sortable_columns
9
+ # list columns inside the Array in string dot notation.
10
+ # Example: 'users.email'
11
+ @sortable_columns ||= []
12
+ end
13
+
14
+ def searchable_columns
15
+ # list columns inside the Array in string dot notation.
16
+ # Example: 'users.email'
17
+ @searchable_columns ||= []
18
+ end
19
+
20
+ private
21
+
22
+ def data
23
+ records.map do |record|
24
+ [
25
+ # comma separated list of the values for each cell of a table row
26
+ # example: record.attribute,
27
+ ]
28
+ end
29
+ end
30
+
31
+ def get_raw_records
32
+ # insert query here
33
+ end
34
+
35
+ # ==== Insert 'presenter'-like methods below if necessary
36
+ end
@@ -1,7 +1,163 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe AjaxDatatablesRails do
4
- describe "as_json" do
5
- it "should return the correct json feed"
3
+ describe AjaxDatatablesRails::Base do
4
+ class Column
5
+ def matches(query)
6
+ []
7
+ end
6
8
  end
7
- end
9
+
10
+ class User
11
+ def self.arel_table
12
+ { :foo => Column.new }
13
+ end
14
+ end
15
+
16
+ params = {
17
+ :sEcho => '0', :sSortDir_0 => 'asc',
18
+ :iSortCol_0 => '1', :iDisplayStart => '11'
19
+ }
20
+ let(:view) { double('view', :params => params) }
21
+
22
+ describe 'an instance' do
23
+ it 'requires a view_context' do
24
+ expect { AjaxDatatablesRails::Base.new }.to raise_error
25
+ end
26
+
27
+ it 'accepts an options hash' do
28
+ datatable = AjaxDatatablesRails::Base.new(view, :foo => 'bar')
29
+ expect(datatable.options).to eq(:foo => 'bar')
30
+ end
31
+ end
32
+
33
+ describe 'helper methods' do
34
+ describe '#offset' do
35
+ it 'defaults to 0' do
36
+ default_view = double('view', :params => {})
37
+ datatable = AjaxDatatablesRails::Base.new(default_view)
38
+ expect(datatable.send(:offset)).to eq(0)
39
+ end
40
+
41
+ it 'matches the value on view params[:iDisplayStart] minus 1' do
42
+ paginated_view = double('view', :params => { :iDisplayStart => '11' })
43
+ datatable = AjaxDatatablesRails::Base.new(paginated_view)
44
+ expect(datatable.send(:offset)).to eq(10)
45
+ end
46
+ end
47
+
48
+ describe '#page' do
49
+ it 'calculates page number from params[:iDisplayStart] and #per_page' do
50
+ paginated_view = double('view', :params => { :iDisplayStart => '11' })
51
+ datatable = AjaxDatatablesRails::Base.new(paginated_view)
52
+ expect(datatable.send(:page)).to eq(2)
53
+ end
54
+ end
55
+
56
+ describe '#per_page' do
57
+ it 'defaults to 10' do
58
+ datatable = AjaxDatatablesRails::Base.new(view)
59
+ expect(datatable.send(:per_page)).to eq(10)
60
+ end
61
+
62
+ it 'matches the value on view params[:iDisplayLength]' do
63
+ other_view = double('view', :params => { :iDisplayLength => 20 })
64
+ datatable = AjaxDatatablesRails::Base.new(other_view)
65
+ expect(datatable.send(:per_page)).to eq(20)
66
+ end
67
+ end
68
+
69
+ describe '#sort_column' do
70
+ it 'returns a column name from the #sorting_columns array' do
71
+ sort_view = double('view', :params => { :iSortCol_0 => '1' })
72
+ datatable = AjaxDatatablesRails::Base.new(view)
73
+ datatable.stub(:sortable_columns) { ['foo', 'bar', 'baz'] }
74
+
75
+ expect(datatable.send(:sort_column)).to eq('bar')
76
+ end
77
+ end
78
+
79
+ describe '#sort_direction' do
80
+ it 'matches value of params[:sSortDir_0]' do
81
+ sorting_view = double('view', :params => { :sSortDir_0 => 'desc' })
82
+ datatable = AjaxDatatablesRails::Base.new(sorting_view)
83
+ expect(datatable.send(:sort_direction)).to eq('DESC')
84
+ end
85
+
86
+ it 'can only be one option from ASC or DESC' do
87
+ sorting_view = double('view', :params => { :sSortDir_0 => 'foo' })
88
+ datatable = AjaxDatatablesRails::Base.new(sorting_view)
89
+ expect(datatable.send(:sort_direction)).to eq('ASC')
90
+ end
91
+ end
92
+
93
+ describe '#sortable_columns' do
94
+ it 'returns an array representing database columns' do
95
+ datatable = AjaxDatatablesRails::Base.new(view)
96
+ expect(datatable.sortable_columns).to eq([])
97
+ end
98
+ end
99
+
100
+ describe '#searchable_columns' do
101
+ it 'returns an array representing database columns' do
102
+ datatable = AjaxDatatablesRails::Base.new(view)
103
+ expect(datatable.searchable_columns).to eq([])
104
+ end
105
+ end
106
+ end
107
+
108
+ describe 'perform' do
109
+ let(:results) { double('Collection', :offset => [], :limit => []) }
110
+ let(:view) { double('view', :params => {}) }
111
+ let(:datatable) { AjaxDatatablesRails::Base.new(view) }
112
+
113
+ describe '#paginate_records' do
114
+ it 'raises a MethodNotImplementedError' do
115
+ expect { datatable.send(:paginate_records, []) }.to raise_error(
116
+ AjaxDatatablesRails::Base::MethodNotImplementedError
117
+ )
118
+ end
119
+ end
120
+
121
+ describe '#sort_records' do
122
+ it 'calls #order on a collection' do
123
+ results.should_receive(:order)
124
+ datatable.send(:sort_records, results)
125
+ end
126
+ end
127
+
128
+ describe '#filter_records' do
129
+ let(:records) { double('User', :where => []) }
130
+ let(:search_view) { double('view', :params => { :sSearch => 'foo' }) }
131
+
132
+ it 'applies search like functionality on a collection' do
133
+ datatable = AjaxDatatablesRails::Base.new(search_view)
134
+ datatable.stub(:searchable_columns) { ['users.foo'] }
135
+
136
+ records.should_receive(:where)
137
+ datatable.send(:filter_records, records)
138
+ end
139
+ end
140
+ end
141
+
142
+ describe 'hook methods' do
143
+ let(:datatable) { AjaxDatatablesRails::Base.new(view) }
144
+
145
+ describe '#data' do
146
+ it 'raises a MethodNotImplementedError' do
147
+ expect { datatable.data }.to raise_error(
148
+ AjaxDatatablesRails::Base::MethodNotImplementedError,
149
+ 'Please implement this method in your class.'
150
+ )
151
+ end
152
+ end
153
+
154
+ describe '#get_raw_records' do
155
+ it 'raises a MethodNotImplementedError' do
156
+ expect { datatable.get_raw_records }.to raise_error(
157
+ AjaxDatatablesRails::Base::MethodNotImplementedError,
158
+ 'Please implement this method in your class.'
159
+ )
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ class KaminariDatatable < AjaxDatatablesRails::Base
4
+ include AjaxDatatablesRails::Extensions::Kaminari
5
+ end
6
+
7
+ describe KaminariDatatable do
8
+ describe '#paginate_records' do
9
+ let(:users_database) do
10
+ double('User',
11
+ :all => double('RecordCollection',
12
+ :page => double('Array', :per => [])
13
+ )
14
+ )
15
+ end
16
+
17
+ let(:datatable) { KaminariDatatable.new(double('view', :params => {})) }
18
+ let(:records) { users_database.all }
19
+
20
+ it 'calls #page on passed record collection' do
21
+ records.should_receive(:page)
22
+ datatable.send(:paginate_records, records)
23
+ end
24
+
25
+ it 'calls #per_page on passed record collection' do
26
+ arry = double('Array', :per => [])
27
+ records.stub(:page).and_return(arry)
28
+ arry.should_receive(:per)
29
+ datatable.send(:paginate_records, records)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+
3
+ class SimplePaginateDatatable < AjaxDatatablesRails::Base
4
+ include AjaxDatatablesRails::Extensions::SimplePaginator
5
+ end
6
+
7
+ describe SimplePaginateDatatable do
8
+ describe '#paginate_records' do
9
+ let(:users_database) do
10
+ double('User',
11
+ :all => double('RecordCollection',
12
+ :offset => double('Array', :limit => [])
13
+ )
14
+ )
15
+ end
16
+
17
+ let(:datatable) { SimplePaginateDatatable.new(double('view', :params => {})) }
18
+ let(:records) { users_database.all }
19
+
20
+ it 'calls #offset on passed record collection' do
21
+ records.should_receive(:offset)
22
+ datatable.send(:paginate_records, records)
23
+ end
24
+
25
+ it 'calls #limit on passed record collection' do
26
+ arry = double('Array', :limit => [])
27
+ records.stub(:offset).and_return(arry)
28
+ arry.should_receive(:limit)
29
+ datatable.send(:paginate_records, records)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ class WillPaginateDatatable < AjaxDatatablesRails::Base
4
+ include AjaxDatatablesRails::Extensions::WillPaginate
5
+ end
6
+
7
+ describe WillPaginateDatatable do
8
+ describe '#paginate_records' do
9
+ let(:users_database) do
10
+ double('User',
11
+ :all => double('RecordCollection',
12
+ :paginate => double('Array', :per_page => [])
13
+ )
14
+ )
15
+ end
16
+
17
+ let(:datatable) { WillPaginateDatatable.new(double('view', :params => {})) }
18
+ let(:records) { users_database.all }
19
+
20
+ it 'calls #page and #per_page on passed record collection' do
21
+ records.should_receive(:paginate).with(:page=>1, :per_page=>10)
22
+ datatable.send(:paginate_records, records)
23
+ end
24
+ end
25
+ end
@@ -1 +1,3 @@
1
- require 'ajax-datatables-rails'
1
+ require 'pry'
2
+ require 'rails'
3
+ require 'ajax-datatables-rails'
metadata CHANGED
@@ -1,32 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ajax-datatables-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Joel Quenneville
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-09-10 00:00:00.000000000 Z
11
+ date: 2014-05-21 00:00:00.000000000 Z
13
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: railties
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
14
27
  - !ruby/object:Gem::Dependency
15
28
  name: rspec
16
29
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
30
  requirements:
19
- - - ! '>='
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: generator_spec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
20
60
  - !ruby/object:Gem::Version
21
61
  version: '0'
22
62
  type: :development
23
63
  prerelease: false
24
64
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
65
  requirements:
27
- - - ! '>='
66
+ - - '>='
28
67
  - !ruby/object:Gem::Version
29
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 3.1.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 3.1.0
30
97
  description: A gem that simplifies using datatables and hundreds of records via ajax
31
98
  email:
32
99
  - joel.quenneville@collegeplus.org
@@ -34,47 +101,51 @@ executables: []
34
101
  extensions: []
35
102
  extra_rdoc_files: []
36
103
  files:
37
- - .gitignore
38
- - .rspec
39
- - .rvmrc
40
- - Gemfile
41
- - LICENSE
42
- - README.md
43
- - Rakefile
44
- - ajax-datatables-rails.gemspec
104
+ - lib/ajax-datatables-rails/base.rb
105
+ - lib/ajax-datatables-rails/extensions/kaminari.rb
106
+ - lib/ajax-datatables-rails/extensions/simple_paginator.rb
107
+ - lib/ajax-datatables-rails/extensions/will_paginate.rb
108
+ - lib/ajax-datatables-rails/version.rb
45
109
  - lib/ajax-datatables-rails.rb
46
- - lib/generators/ajaxdatatable/USAGE
47
- - lib/generators/ajaxdatatable/ajaxdatatable_generator.rb
48
- - lib/generators/ajaxdatatable/templates/datatable.rb
49
- - lib/generators/filterdatatable/USAGE
50
- - lib/generators/filterdatatable/templates/filter.rb
110
+ - lib/generators/rails/datatable_generator.rb
111
+ - lib/generators/rails/templates/datatable.rb
51
112
  - spec/ajax-datatables-rails/ajax_datatables_rails_spec.rb
113
+ - spec/ajax-datatables-rails/kaminari_spec.rb
114
+ - spec/ajax-datatables-rails/simple_paginator_spec.rb
115
+ - spec/ajax-datatables-rails/will_paginate_spec.rb
52
116
  - spec/spec_helper.rb
117
+ - CHANGELOG.md
118
+ - Gemfile
119
+ - LICENSE
120
+ - Rakefile
121
+ - README.md
53
122
  homepage: ''
54
123
  licenses: []
124
+ metadata: {}
55
125
  post_install_message:
56
126
  rdoc_options: []
57
127
  require_paths:
58
128
  - lib
59
129
  required_ruby_version: !ruby/object:Gem::Requirement
60
- none: false
61
130
  requirements:
62
- - - ! '>='
131
+ - - '>='
63
132
  - !ruby/object:Gem::Version
64
- version: '0'
133
+ version: 1.9.2
65
134
  required_rubygems_version: !ruby/object:Gem::Requirement
66
- none: false
67
135
  requirements:
68
- - - ! '>='
136
+ - - '>='
69
137
  - !ruby/object:Gem::Version
70
138
  version: '0'
71
139
  requirements: []
72
140
  rubyforge_project:
73
- rubygems_version: 1.8.21
141
+ rubygems_version: 2.1.11
74
142
  signing_key:
75
- specification_version: 3
143
+ specification_version: 4
76
144
  summary: A wrapper around datatable's ajax methods that allow synchronization with
77
145
  server-side pagination in a rails app
78
146
  test_files:
79
147
  - spec/ajax-datatables-rails/ajax_datatables_rails_spec.rb
148
+ - spec/ajax-datatables-rails/kaminari_spec.rb
149
+ - spec/ajax-datatables-rails/simple_paginator_spec.rb
150
+ - spec/ajax-datatables-rails/will_paginate_spec.rb
80
151
  - spec/spec_helper.rb
data/.gitignore DELETED
@@ -1,17 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color
data/.rvmrc DELETED
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
- # development environment upon cd'ing into the directory
5
-
6
- # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
- # Only full ruby name is supported here, for short names use:
8
- # echo "rvm use 1.9.3" > .rvmrc
9
- environment_id="ruby-1.9.3-p125@ajax-datatables-rails"
10
-
11
- # Uncomment the following lines if you want to verify rvm version per project
12
- # rvmrc_rvm_version="1.11.0-pre" # 1.10.1 seams as a safe start
13
- # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
- # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
- # return 1
16
- # }
17
-
18
- # First we attempt to load the desired environment directly from the environment
19
- # file. This is very fast and efficient compared to running through the entire
20
- # CLI and selector. If you want feedback on which environment was used then
21
- # insert the word 'use' after --create as this triggers verbose mode.
22
- if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
- && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
- then
25
- \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
- [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
- \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
- else
29
- # If the environment file has not yet been created, use the RVM CLI to select.
30
- rvm --create "$environment_id" || {
31
- echo "Failed to create RVM environment '${environment_id}'."
32
- return 1
33
- }
34
- fi
35
-
36
- # If you use bundler, this might be useful to you:
37
- # if [[ -s Gemfile ]] && {
38
- # ! builtin command -v bundle >/dev/null ||
39
- # builtin command -v bundle | grep $rvm_path/bin/bundle >/dev/null
40
- # }
41
- # then
42
- # printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
43
- # gem install bundler
44
- # fi
45
- # if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
46
- # then
47
- # bundle install | grep -vE '^Using|Your bundle is complete'
48
- # fi
@@ -1,19 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/ajax-datatables-rails', __FILE__)
3
-
4
- Gem::Specification.new do |gem|
5
- gem.authors = ["Joel Quenneville"]
6
- gem.email = ["joel.quenneville@collegeplus.org"]
7
- gem.description = %q{A gem that simplifies using datatables and hundreds of records via ajax}
8
- gem.summary = %q{A wrapper around datatable's ajax methods that allow synchronization with server-side pagination in a rails app}
9
- gem.homepage = ""
10
-
11
- gem.files = `git ls-files`.split($\)
12
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
- gem.name = "ajax-datatables-rails"
15
- gem.require_paths = ["lib"]
16
- gem.version = AjaxDatatablesRails::VERSION
17
-
18
- gem.add_development_dependency "rspec"
19
- end
@@ -1,2 +0,0 @@
1
- Description:
2
- The ajaxdatatable generator creates a *_datatable model in the app/datatables directory. This class inherits from AjaxDatatablesRails
@@ -1,8 +0,0 @@
1
- class AjaxdatatableGenerator < Rails::Generators::Base
2
- source_root File.expand_path('../templates', __FILE__)
3
- argument :model, type: :string
4
-
5
- def generate_ajaxdatatable
6
- template 'datatable.rb', File.join('app/datatables', "#{model.tableize}_datatable.rb")
7
- end
8
- end
@@ -1,33 +0,0 @@
1
- class <%= model.classify.pluralize %>Datatable < AjaxDatatablesRails
2
-
3
- def initialize(view)
4
- @model_name = <%= model.classify %>
5
- @columns = # insert array of column names here
6
- @searchable_columns = #insert array of columns that will be searched
7
- super(view)
8
- end
9
-
10
- private
11
-
12
- def data
13
- <%= model.tableize %>.map do |<%= model.tableize.singularize %>|
14
- [
15
- # comma separated list of the values for each cell of a table row
16
- ]
17
- end
18
- end
19
-
20
- def <%= model.tableize %>
21
- @<%= model.tableize %> ||= fetch_records
22
- end
23
-
24
- def get_raw_records
25
- # insert query here
26
- end
27
-
28
- def get_raw_record_count
29
- search_records(get_raw_records).count
30
- end
31
-
32
- # ==== Insert 'presenter'-like methods below if necessary
33
- end
@@ -1,2 +0,0 @@
1
- Description:
2
- The ajaxdatatable generator creates a *_filter_datatable model in the app/datatables directory. This class overwrites the query in it's parent class
@@ -1,8 +0,0 @@
1
- class <%= model.classify.pluralize %>FilterDatatable < <%= model.classify.pluralize %>Datatable
2
-
3
- private
4
-
5
- def get_raw_records
6
- # insert query here
7
- end
8
- end