activeadmin-orderable-table 0.0.1

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: 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: []