activeadmin-orderable-table 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1d219b5422a68bd3eb67ef48836e3b9792d00515
4
+ data.tar.gz: 1442df0bc372616af4422f4210c82542b9203082
5
+ SHA512:
6
+ metadata.gz: 94f61daf00fbf7d6753012f2bb783ac5e59b42128bd26e5ecfc9f76f2cb26d74c2650922d2c0eabed71ca90d6b65abe5cf94e9d68cc5e19691fb1c97b05d2d5e
7
+ data.tar.gz: fb6d9c701d956a26ef8807b6131a9c6f814810d93de07d4cd405874b4eefdd355a56b40624af89b41519f70b87e7b306b677c28a52973669b76d4881e7cc0dff
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ .ruby-gemset
5
+ .ruby-version
6
+ .idea
7
+ .directory
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'activeadmin', git: 'https://github.com/activeadmin/activeadmin.git'
6
+ gem 'inherited_resources', git: 'https://github.com/activeadmin/inherited_resources'
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Max Hordiichuk
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,61 @@
1
+ # Active Admin Orderable Table
2
+
3
+ This gem extends ActiveAdmin so that your index page's table rows can be
4
+ orderable without page reloading via a drag-and-drop interface.
5
+
6
+ ## Usage
7
+
8
+ ### Add gem to your Gemfile
9
+
10
+ ```ruby
11
+ gem 'sortable-rails'
12
+ gem 'activeadmin-orderable-table'
13
+ ```
14
+
15
+ ### Include the JavaScript in active_admin.js
16
+
17
+ ```javascript
18
+ //= require sortable-rails
19
+ //= require activeadmin-orderable-table
20
+ ```
21
+
22
+ ### Include the Stylesheet in active_admin.css
23
+ ```css
24
+ //= require activeadmin-orderable-table
25
+ ```
26
+
27
+ ### Configure your ActiveRecord model
28
+ You need to add an ordinal column to desired table:
29
+ ```bash
30
+ rails g migration AddOrdinalToPage ordinal:integer
31
+ rake db:migrate
32
+ ```
33
+
34
+ Then add following line to model that suppose to be orderable:
35
+ ```ruby
36
+ acts_as_orderable_table
37
+ ```
38
+
39
+ ### Configure your ActiveAdmin Resource
40
+
41
+ ```ruby
42
+ ActiveAdmin.register Page do
43
+ config.sort_order = 'ordinal_asc'
44
+ config.paginate = false # optional; drag-and-drop across pages is not supported
45
+
46
+ orderable # creates the controller action which handles the ordering
47
+
48
+ index do
49
+ orderable_handle_column # inserts a drag handle
50
+ # other columns...
51
+ end
52
+ end
53
+ ```
54
+
55
+ ## Contributing
56
+
57
+ 1. Fork it
58
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
59
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
60
+ 4. Push to the branch (`git push origin my-new-feature`)
61
+ 5. Create new Pull Request
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/activeadmin-orderable-table/version', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'activeadmin-orderable-table'
6
+ s.version = Activeadmin::OrderableTable::VERSION
7
+ s.date = Time.new.strftime('%Y-%m-%d')
8
+ s.summary = 'Order ActiveAdmin tables'
9
+ s.description = 'Drag and drop order interface for ActiveAdmin tables'
10
+ s.authors = ['Max Hordiichuk']
11
+ s.email = 'hordijchuk.m.i@gmail.com'
12
+ s.files = `git ls-files`.split("\n")
13
+ s.homepage = 'https://github.com/MaximHordijchuk/activeadmin-orderable-table'
14
+ s.license = 'MIT'
15
+
16
+ s.add_development_dependency 'bundler', '~> 1.10'
17
+ s.add_development_dependency 'rspec-rails', '~> 3.5'
18
+ s.add_development_dependency 'test-unit', '~> 3.0'
19
+ s.add_development_dependency 'sqlite3', '~> 1.3'
20
+ s.add_development_dependency 'sass-rails'
21
+ s.add_development_dependency 'activerecord'
22
+
23
+ s.add_runtime_dependency 'sortable-rails', '~> 1.4'
24
+ end
@@ -0,0 +1,97 @@
1
+ (function($) {
2
+ $(document).ready(function() {
3
+ var $handle = $('.activeadmin-orderable-handle');
4
+ if ($handle.length > 0) {
5
+ $handle.closest('tbody').activeAdminOrderableTable();
6
+ }
7
+ });
8
+
9
+ $.fn.activeAdminOrderableTable = function() {
10
+ var $container = this;
11
+
12
+ // Returns all sibling elements between $firstElement and $lastElement
13
+ // including these elements
14
+ var findElements = function ($container, $firstElement, $lastElement) {
15
+ var $elements = $firstElement.nextUntil($lastElement);
16
+ var elements = [$firstElement[0]];
17
+ for (var i = 0; i < $elements.length; i++) {
18
+ elements.push($elements[i]);
19
+ }
20
+ elements.push($lastElement[0]);
21
+ return elements;
22
+ };
23
+
24
+ // [1, 2, 3, 4, 5] -> [2, 3, 4, 5, 1]
25
+ // Reverse: [1, 2, 3, 4, 5] -> [5, 1, 2, 3, 4]
26
+ var rotateArray = function (elements, reverse) {
27
+ var result = [], i;
28
+ if (reverse) {
29
+ result.push(elements[elements.length - 1]);
30
+ for (i = 0; i < elements.length - 1; i++) {
31
+ result.push(elements[i]);
32
+ }
33
+ } else {
34
+ for (i = 1; i < elements.length; i++) {
35
+ result.push(elements[i]);
36
+ }
37
+ result.push(elements[0]);
38
+ }
39
+ return result;
40
+ };
41
+
42
+ // Returns array of elements' ordinal values
43
+ var getOrdinals = function (elements) {
44
+ var ordinals = [];
45
+ for (var i = 0; i < elements.length; i++) {
46
+ ordinals.push($(elements[i]).find('[data-ordinal]').data('ordinal'));
47
+ }
48
+ return ordinals;
49
+ };
50
+
51
+ var setOrdinals = function (elements, ordinals) {
52
+ for (var i = 0; i < elements.length; i++) {
53
+ $(elements[i]).find('[data-ordinal]').data('ordinal', ordinals[i]);
54
+ }
55
+ };
56
+
57
+ var sendReorderRequest = function ($element, ordinals) {
58
+ var url = $element.find('[data-reorder-url]').data('reorder-url');
59
+ $.ajax({
60
+ url: url,
61
+ type: 'post',
62
+ data: { position: $element.find('[data-ordinal]').data('ordinal'),
63
+ ordinals: ordinals },
64
+ error: function(error) { console.log('Reordering failed:', error) }
65
+ });
66
+ };
67
+
68
+ var logOrdinals = function (elements) {
69
+ for (var i = 0; i < elements.length; i++) {
70
+ console.log($(elements[i]).find('[data-ordinal]').data('ordinal'));
71
+ }
72
+ };
73
+
74
+ Sortable.create($container[0], {
75
+ animation: 150,
76
+ draggable: 'tr',
77
+ onEnd: function (event) {
78
+ if (event.oldIndex != event.newIndex) {
79
+ var $firstElement, $lastElement, $movedElement, elements, ordinals;
80
+ if (event.oldIndex < event.newIndex) { // from left to right
81
+ $firstElement = $container.find('tr').eq(event.oldIndex);
82
+ $lastElement = $container.find('tr').eq(event.newIndex);
83
+ $movedElement = $lastElement
84
+ } else { // from right to left
85
+ $firstElement = $container.find('tr').eq(event.newIndex);
86
+ $lastElement = $container.find('tr').eq(event.oldIndex);
87
+ $movedElement = $firstElement
88
+ }
89
+ elements = findElements($container, $firstElement, $lastElement);
90
+ ordinals = rotateArray(getOrdinals(elements), event.oldIndex < event.newIndex);
91
+ setOrdinals(elements, ordinals);
92
+ sendReorderRequest($movedElement, ordinals);
93
+ }
94
+ }
95
+ });
96
+ }
97
+ })(jQuery);
@@ -0,0 +1 @@
1
+ .activeadmin-orderable-column .activeadmin-orderable-handle { cursor: ns-resize; }
@@ -0,0 +1,42 @@
1
+ if defined?(ActiveAdmin)
2
+ module ActiveAdmin
3
+ module OrderableTable
4
+ module ControllerActions
5
+ def orderable
6
+ member_action :reorder, method: :post do
7
+ position = params[:position].to_i
8
+ if params[:ordinals]
9
+ ordinals_scope = params[:ordinals].map { |ordinal| ordinal.to_i }
10
+ resource.insert_at position, ordinals_scope
11
+ else
12
+ resource.insert_at position
13
+ end
14
+ head 200
15
+ end
16
+ end
17
+ end
18
+
19
+ module TableMethods
20
+ HANDLE = '&#x2195;'.html_safe
21
+
22
+ def orderable_handle_column
23
+ column_name = resource_class.ordinal_field
24
+ return if params[:order] != "#{column_name}_asc" && params[:order] != "#{column_name}_desc"
25
+
26
+ column '', class: 'activeadmin-orderable-column' do |resource|
27
+ reorder_path = resource_path(resource) + '/reorder'
28
+ content_tag :span, HANDLE, class: 'activeadmin-orderable-handle', 'data-reorder-url': reorder_path,
29
+ 'data-ordinal': resource.acts_ordinal_value
30
+ end
31
+ end
32
+ end
33
+
34
+ ::ActiveAdmin::ResourceDSL.send(:include, ControllerActions)
35
+ ::ActiveAdmin::Views::TableFor.send(:include, TableMethods)
36
+
37
+ class Engine < ::Rails::Engine
38
+ # Including an Engine tells Rails that this gem contains assets
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ require 'active_record/base'
2
+
3
+ module ActiveRecord
4
+ class Base
5
+ include ActsAs::OrderableTable
6
+ end
7
+ end
@@ -0,0 +1,93 @@
1
+ module ActiveRecord
2
+ module ActsAs
3
+ module OrderableTable
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ attr_reader :ordinal_field, :starts_from
10
+
11
+ def acts_as_orderable_table(options = {})
12
+ @ordinal_field = options[:ordinal_field] || :ordinal
13
+ @starts_from = options[:starts_from] || 0
14
+
15
+ validates @ordinal_field, presence: true
16
+ validate :check_ordinal_uniqueness
17
+ before_validation :set_defaults
18
+
19
+ class_eval do
20
+ include ActiveRecord::ActsAs::OrderableTable::InstanceMethods
21
+ end
22
+ end
23
+ end
24
+
25
+ module InstanceMethods
26
+ def insert_at(position, ordinals_scope = nil)
27
+ return if position == acts_ordinal_value # if ordinal haven't changed
28
+
29
+ # if new position is not occupied just take this ordinal
30
+ unless self.class.where("#{acts_ordinal_field}": position).first
31
+ update_attributes("#{acts_ordinal_field}": position)
32
+ return
33
+ end
34
+
35
+ items = items_scoped(position, ordinals_scope)
36
+ current_positions = items.map { |item| item.send(acts_ordinal_field) }
37
+ reordered_positions = reorder_positions(position, current_positions)
38
+ update_ordinals(items, reordered_positions)
39
+ end
40
+
41
+ def acts_ordinal_field
42
+ self.class.ordinal_field
43
+ end
44
+
45
+ def acts_ordinal_value
46
+ self.send(acts_ordinal_field)
47
+ end
48
+
49
+ def starts_from
50
+ self.class.starts_from
51
+ end
52
+
53
+ private
54
+
55
+ def ordinal_range(position)
56
+ position > acts_ordinal_value ? (acts_ordinal_value + 1)..position : position...acts_ordinal_value
57
+ end
58
+
59
+ def items_scoped(position, ordinals_scope)
60
+ actual_ordinals_scope = *ordinal_range(position)
61
+ actual_ordinals_scope &= ordinals_scope if ordinals_scope
62
+ self.class.where("#{acts_ordinal_field}": actual_ordinals_scope).order(acts_ordinal_field).to_a.push(self)
63
+ end
64
+
65
+ def reorder_positions(position, positions)
66
+ position > acts_ordinal_value ? positions.rotate(-1) : positions.rotate
67
+ end
68
+
69
+ def update_ordinals(items, positions)
70
+ items.each_with_index do |item, index|
71
+ item.assign_attributes("#{acts_ordinal_field}": positions[index])
72
+ item.save!(validate: false)
73
+ end
74
+ end
75
+
76
+ def check_ordinal_uniqueness
77
+ if acts_ordinal_value.present? &&
78
+ self.class.where("#{acts_ordinal_field}": acts_ordinal_value).reject { |record| record == self }.first
79
+ self.errors[acts_ordinal_field] << 'must be unique'
80
+ end
81
+ end
82
+
83
+ def set_defaults
84
+ if acts_ordinal_value.blank?
85
+ ordinal_value_prev = self.class.maximum(acts_ordinal_field)
86
+ ordinal_value_next = (ordinal_value_prev ? ordinal_value_prev + 1 : starts_from)
87
+ assign_attributes("#{acts_ordinal_field}": ordinal_value_next)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,7 @@
1
+ require 'activeadmin-orderable-table/version'
2
+ require 'active_record'
3
+ require 'active_record/railtie'
4
+ require 'rails/engine'
5
+ require 'active_record/acts_as/orderable_table'
6
+ require 'active_record/acts_as'
7
+ require 'active_admin/orderable_table'
@@ -0,0 +1,5 @@
1
+ module Activeadmin
2
+ module OrderableTable
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ require 'sqlite3'
2
+
3
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
4
+
5
+ ActiveRecord::Schema.define(:version => 1) do
6
+ create_table "entities", :force => true do |t|
7
+ t.integer "ordinal", null: false
8
+ t.integer "field"
9
+ end
10
+ end
11
+
12
+ class Entity < ActiveRecord::Base
13
+ acts_as_orderable_table
14
+ end
@@ -0,0 +1,181 @@
1
+ require 'spec_helper'
2
+
3
+ RANDOM_ORDINAL_MAX = 100
4
+ # should be > 2 and <= RANDOM_ORDINAL_MAX
5
+ TEST_ENTITIES_COUNT = 10
6
+ # should be > 1 and <= TEST_ENTITIES_COUNT
7
+ TEST_ENTITIES_SCOPE_COUNT = 6
8
+
9
+ RSpec.describe ActiveRecord::ActsAs::OrderableTable do
10
+ describe 'without options' do
11
+ let(:random_ordinal) { Random.new.rand(RANDOM_ORDINAL_MAX) }
12
+
13
+ after { Entity.destroy_all }
14
+
15
+ describe 'set default values' do
16
+ it 'should create entity with custom ordinal value' do
17
+ entity = Entity.create!(ordinal: random_ordinal)
18
+ expect(entity.ordinal).to eq random_ordinal
19
+ end
20
+
21
+ it 'should set default ordinal value to zero with empty entities table' do
22
+ entity = Entity.create!
23
+ expect(entity.ordinal).to eq 0
24
+ end
25
+
26
+ it 'should set default ordinal value to incremented max existing ordinal' do
27
+ entities_with_random_ordinal(TEST_ENTITIES_COUNT, RANDOM_ORDINAL_MAX).each(&:save!)
28
+
29
+ entities = [Entity.new(ordinal: RANDOM_ORDINAL_MAX + random_ordinal)]
30
+ (TEST_ENTITIES_COUNT).times { entities << Entity.new }
31
+ entities.each(&:save!)
32
+
33
+ 1.upto(entities.size - 1) { |i| expect(entities[i].ordinal).to eq entities[i - 1].ordinal + 1 }
34
+ end
35
+ end
36
+
37
+ it 'should validate ordinal uniqueness' do
38
+ Entity.create!(ordinal: random_ordinal)
39
+
40
+ entity_with_different_ordinal = Entity.new(ordinal: random_ordinal + 1)
41
+ expect(entity_with_different_ordinal).to be_truthy
42
+
43
+ entity_with_same_ordinal = Entity.new(ordinal: random_ordinal)
44
+ expect(entity_with_same_ordinal.valid?).to be_falsey
45
+ end
46
+
47
+ describe 'reordering' do
48
+ context 'without scope' do
49
+ let(:entities) do
50
+ entities = entities_with_random_ordinal(TEST_ENTITIES_COUNT, RANDOM_ORDINAL_MAX)
51
+ entities.each(&:save!)
52
+ end
53
+
54
+ let(:entity) { entities.first }
55
+
56
+ it "shouldn't change position if ordinal haven't changed" do
57
+ expect { entity.insert_at(entity.ordinal) }.not_to change { entities.map(&:ordinal) }
58
+ end
59
+
60
+ context 'new position is now occupied' do
61
+ let(:position_new) { RANDOM_ORDINAL_MAX + random_ordinal }
62
+
63
+ it 'should not change other entities if new position is now occupied' do
64
+ expect { entity.insert_at(position_new) }.not_to change { entities.drop(1).map(&:ordinal) }
65
+ end
66
+
67
+ it 'should set new position if new position is now occupied' do
68
+ entity.insert_at(position_new)
69
+ expect(entity.ordinal).to eq position_new
70
+ end
71
+ end
72
+
73
+ context 'when new position is occupied' do
74
+ let(:sorted_scope) { entities.sort_by { |entity| entity.ordinal } }
75
+
76
+ it 'should correctly reorder close entities when entity moved down' do
77
+ scope = sorted_scope.take(2)
78
+ reordered_ids = scope.map(&:id).reverse! + sorted_scope.drop(2).map(&:id)
79
+ scope.first.insert_at(scope.second.ordinal)
80
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
81
+ end
82
+
83
+ it 'should correctly reorder close entities when entity moved up' do
84
+ scope = sorted_scope.take(2)
85
+ reordered_ids = scope.map(&:id).reverse! + sorted_scope.drop(2).map(&:id)
86
+ scope.second.insert_at(scope.first.ordinal)
87
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
88
+ end
89
+
90
+ it 'should correctly reorder entities when entity moved down' do
91
+ scope = sorted_scope.take(TEST_ENTITIES_SCOPE_COUNT)
92
+ # [1, 2, 3, 4, 5].rotate => [2, 3, 4, 5, 1]
93
+ reordered_ids = scope.map(&:id).rotate + sorted_scope.drop(TEST_ENTITIES_SCOPE_COUNT).map(&:id)
94
+ scope.first.insert_at(scope.last.ordinal)
95
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
96
+ end
97
+
98
+ it 'should correctly reorder entities when entity moved up' do
99
+ scope = sorted_scope.take(TEST_ENTITIES_SCOPE_COUNT)
100
+ # [1, 2, 3, 4, 5].rotate(-1) => [5, 1, 2, 3, 4]
101
+ reordered_ids = scope.map(&:id).rotate(-1) + sorted_scope.drop(TEST_ENTITIES_SCOPE_COUNT).map(&:id)
102
+ scope.last.insert_at(scope.first.ordinal)
103
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
104
+ end
105
+ end
106
+ end
107
+
108
+ context 'with scope' do
109
+ let(:entities) do
110
+ entities = entities_with_random_ordinal(TEST_ENTITIES_COUNT, RANDOM_ORDINAL_MAX)
111
+ entities.each(&:save!)
112
+ end
113
+
114
+ let(:entity) { entities.first }
115
+
116
+ it "shouldn't change position if ordinal haven't changed" do
117
+ expect { entity.insert_at(entity.ordinal, [entity.ordinal]) }
118
+ .not_to change { entities.map(&:ordinal) }
119
+ end
120
+
121
+ context 'new position is now occupied' do
122
+ let(:position_new) { RANDOM_ORDINAL_MAX + random_ordinal }
123
+
124
+ it 'should not change other entities if new position is now occupied' do
125
+ expect { entity.insert_at(position_new, entity.ordinal) }
126
+ .not_to change { entities.drop(1).map(&:ordinal) }
127
+ end
128
+
129
+ it 'should set new position if new position is now occupied' do
130
+ entity.insert_at(position_new, entity.ordinal)
131
+ expect(entity.ordinal).to eq position_new
132
+ end
133
+ end
134
+
135
+ context 'when new position is occupied' do
136
+ let(:sorted_scope) { entities.sort_by { |entity| entity.ordinal } }
137
+
138
+ it 'should correctly reorder close entities when entity moved down' do
139
+ scope = sorted_scope.take(2)
140
+ reordered_ids = scope.map(&:id).reverse! + sorted_scope.drop(2).map(&:id)
141
+ scope.first.insert_at(scope.second.ordinal, scope.map(&:ordinal))
142
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
143
+ end
144
+
145
+ it 'should correctly reorder close entities when entity moved up' do
146
+ scope = sorted_scope.take(2)
147
+ reordered_ids = scope.map(&:id).reverse! + sorted_scope.drop(2).map(&:id)
148
+ scope.second.insert_at(scope.first.ordinal, scope.map(&:ordinal))
149
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
150
+ end
151
+
152
+ it 'should correctly reorder entities when entity moved down' do
153
+ scope = sorted_scope.take(TEST_ENTITIES_SCOPE_COUNT)
154
+ # [1, 2, 3, 4, 5].rotate => [2, 3, 4, 5, 1]
155
+ reordered_ids = scope.map(&:id).rotate + sorted_scope.drop(TEST_ENTITIES_SCOPE_COUNT).map(&:id)
156
+ scope.first.insert_at(scope.last.ordinal, scope.map(&:ordinal))
157
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
158
+ end
159
+
160
+ it 'should correctly reorder entities when entity moved up' do
161
+ scope = sorted_scope.take(TEST_ENTITIES_SCOPE_COUNT)
162
+ # [1, 2, 3, 4, 5].rotate(-1) => [5, 1, 2, 3, 4]
163
+ reordered_ids = scope.map(&:id).rotate(-1) + sorted_scope.drop(TEST_ENTITIES_SCOPE_COUNT).map(&:id)
164
+ scope.last.insert_at(scope.first.ordinal, scope.map(&:ordinal))
165
+ expect(Entity.order(:ordinal).map(&:id)).to eq reordered_ids
166
+ end
167
+
168
+ it 'should reorder entities only from scope when entity moved down' do
169
+ entities.shuffle!
170
+ scope = entities.take(TEST_ENTITIES_SCOPE_COUNT).sort_by { |entity| entity.ordinal }
171
+ # [1, 2, 3, 4, 5].rotate => [2, 3, 4, 5, 1]
172
+ reordered_ids = scope.map(&:id).rotate
173
+ expect { scope.first.insert_at(scope.last.ordinal, scope.map(&:ordinal)) }
174
+ .not_to change { entities.drop(TEST_ENTITIES_SCOPE_COUNT).map(&:ordinal) }
175
+ expect(Entity.where(id: reordered_ids).order(:ordinal).map(&:id)).to eq reordered_ids
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,10 @@
1
+ require 'activeadmin-orderable-table'
2
+ require 'activeadmin'
3
+ require 'db_helper'
4
+ require 'rspec/rails'
5
+ require 'support/utilities'
6
+
7
+ RSpec.configure do |config|
8
+ config.warnings = true
9
+ config.order = :random
10
+ end
@@ -0,0 +1,4 @@
1
+ def entities_with_random_ordinal(count, random_ordinal_max)
2
+ random_ordinals = (0...random_ordinal_max).to_a.sample(count)
3
+ random_ordinals.map { |random_ordinal| Entity.new(ordinal: random_ordinal) }
4
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activeadmin-orderable-table
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Max Hordiichuk
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-rails
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sqlite3
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sass-rails
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: activerecord
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: sortable-rails
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.4'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.4'
111
+ description: Drag and drop order interface for ActiveAdmin tables
112
+ email: hordijchuk.m.i@gmail.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - ".gitignore"
118
+ - ".rspec"
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - activeadmin-orderable-table.gemspec
123
+ - app/assets/javascripts/activeadmin-orderable-table.js
124
+ - app/assets/stylesheets/activeadmin-orderable-table.css
125
+ - lib/active_admin/orderable_table.rb
126
+ - lib/active_record/acts_as.rb
127
+ - lib/active_record/acts_as/orderable_table.rb
128
+ - lib/activeadmin-orderable-table.rb
129
+ - lib/activeadmin-orderable-table/version.rb
130
+ - spec/db_helper.rb
131
+ - spec/lib/active_record/acts_as/orderable_table_spec.rb
132
+ - spec/spec_helper.rb
133
+ - spec/support/utilities.rb
134
+ homepage: https://github.com/MaximHordijchuk/activeadmin-orderable-table
135
+ licenses:
136
+ - MIT
137
+ metadata: {}
138
+ post_install_message:
139
+ rdoc_options: []
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 2.6.10
155
+ signing_key:
156
+ specification_version: 4
157
+ summary: Order ActiveAdmin tables
158
+ test_files: []