diff --git a/Gemfile b/Gemfile
index 20c5d36..886a406 100644
--- a/Gemfile
+++ b/Gemfile
@@ -8,6 +8,10 @@ end
gem 'jquery-rails'
gem 'jquery-ui-rails'
+if ENV['RAILS_VERSION'] == '~> 3.2.0' && (RUBY_VERSION == '1.9.3' || defined?(JRUBY_VERSION))
+ gem 'mime-types-data', '3.2015.1120'
+end
+
if defined?(JRUBY_VERSION)
gem 'activerecord-jdbcsqlite3-adapter'
else
diff --git a/README.md b/README.md
index a321f4d..d90475a 100644
--- a/README.md
+++ b/README.md
@@ -181,7 +181,9 @@ $(document).ready(function(){
Before send ajax request to server jQuery UI Sortable disabled, after receive response enable.
-`activerecord_sortable()` accetps [JQuery UI Sortable](http://api.jqueryui.com/sortable/)-style options. By default `axis` uses `y` value (to prevent horizontal dragging), and `update` overwrites by internal handler and is unavailabe to set.
+`activerecord_sortable()` accepts [JQuery UI Sortable](http://api.jqueryui.com/sortable/)-style options. By default `axis` uses `y` value (to prevent horizontal dragging), and `update` overwrites by internal handler and is unavailabe to set.
+
+`$('*[data-role=activerecord_sortable]')` node may have `start-position` data-attribute (`
...
`) in this case first element position will be equal to data-attribute value. If `start-position` not set, then first child node data-attribute `position` will be taken, `0` otherwise.
## How to add activerecord-sortable into existing model
diff --git a/README.ru.md b/README.ru.md
index 5f2eb64..65320e2 100644
--- a/README.ru.md
+++ b/README.ru.md
@@ -184,6 +184,8 @@ $(document).ready(function(){
`activerecord_sortable()` принимает аргумент-объект с опциями, аналогичными [виджету JQuery UI Sortable](http://api.jqueryui.com/sortable/). По-умолчанию поле `axis` у аргумента-объекта принимает значение `y` (разрешается только перетаскивание по вертикали), а поле `update` перезаписывается внутренним обработчиком – его переопределить нельзя.
+У узла `$('*[data-role=activerecord_sortable]')` можно указать data-атрибут `start-position` – `...
` – в этом случае позиция первого элемента будет равна значению этого атрибута. Если значение `start-position` не задавать, то будет взято значение data-атрибута `position` первого узла-ребёнка, если и его нет – то `0`.
+
## Как добавить функциональность activerecord-sortable в существующую модель
```ruby
diff --git a/activerecord-sortable.gemspec b/activerecord-sortable.gemspec
index 6c20941..3343047 100644
--- a/activerecord-sortable.gemspec
+++ b/activerecord-sortable.gemspec
@@ -15,7 +15,9 @@ Gem::Specification.new do |gem|
gem.test_files = Dir["spec/**/*"]
gem.add_development_dependency 'capybara'
- gem.add_development_dependency 'poltergeist'
+ gem.add_development_dependency 'poltergeist', '1.7.0'
+ gem.add_development_dependency 'phantomjs', '2.1.1.0'
+
gem.add_development_dependency 'coveralls'
gem.add_development_dependency 'rubocop'
end
diff --git a/lib/activerecord/sortable/acts_as_sortable.rb b/lib/activerecord/sortable/acts_as_sortable.rb
index b9f3882..9fd0502 100644
--- a/lib/activerecord/sortable/acts_as_sortable.rb
+++ b/lib/activerecord/sortable/acts_as_sortable.rb
@@ -8,14 +8,14 @@ module ActsAsSortable
extend ::ActiveSupport::Concern
module ClassMethods
- def acts_as_sortable(&block)
+ def acts_as_sortable
options = {
relation: ->(instance) { instance.class },
append: false,
position_column: :position,
touch: true
}
- block.call(options) if block_given?
+ yield(options) if block_given?
sortable_relation_create_accessors
sortable_relation_provide_accessor_values(options)
diff --git a/lib/activerecord/sortable/version.rb b/lib/activerecord/sortable/version.rb
index 7eb9113..7dc2e5c 100644
--- a/lib/activerecord/sortable/version.rb
+++ b/lib/activerecord/sortable/version.rb
@@ -1,5 +1,5 @@
module ActiveRecord
module Sortable
- VERSION = '0.0.8'
+ VERSION = '0.0.8'.freeze
end
end
diff --git a/lib/assets/javascripts/sortable.js b/lib/assets/javascripts/sortable.js
index 5364e35..54a96ce 100644
--- a/lib/assets/javascripts/sortable.js
+++ b/lib/assets/javascripts/sortable.js
@@ -93,6 +93,7 @@
function Sortable(node, options) {
this.node = node;
+ this.start_position = this.node.data('start-position') || this.node.children().eq(0).data('position') || 0;
if (!options) options = {};
options['update'] = $.proxy(this.sortable_stop_handler, this);
@@ -107,7 +108,7 @@
this.node.sortable('disable').trigger('sortable:start');
var node = ui.item;
- var new_position = node.index();
+ var new_position = this.start_position + node.index();
var old_position = parseInt(node.data('position'));
var positions_updater = new PositionsUpdater(this.node, node, new_position, old_position);
diff --git a/spec/dummy-3.2/app/controllers/things_controller.rb b/spec/dummy-3.2/app/controllers/things_controller.rb
index fa691f9..6f190f4 100644
--- a/spec/dummy-3.2/app/controllers/things_controller.rb
+++ b/spec/dummy-3.2/app/controllers/things_controller.rb
@@ -1,6 +1,7 @@
class ThingsController < ApplicationController
def index
@things = Thing.ordered_by_position_asc
+ @things = @things.where('position >= ?', params[:min_position]) if params[:min_position]
end
def move
diff --git a/spec/dummy-4.0/app/controllers/things_controller.rb b/spec/dummy-4.0/app/controllers/things_controller.rb
index fa691f9..6f190f4 100644
--- a/spec/dummy-4.0/app/controllers/things_controller.rb
+++ b/spec/dummy-4.0/app/controllers/things_controller.rb
@@ -1,6 +1,7 @@
class ThingsController < ApplicationController
def index
@things = Thing.ordered_by_position_asc
+ @things = @things.where('position >= ?', params[:min_position]) if params[:min_position]
end
def move
diff --git a/spec/dummy-4.1/app/controllers/things_controller.rb b/spec/dummy-4.1/app/controllers/things_controller.rb
index fa691f9..6f190f4 100644
--- a/spec/dummy-4.1/app/controllers/things_controller.rb
+++ b/spec/dummy-4.1/app/controllers/things_controller.rb
@@ -1,6 +1,7 @@
class ThingsController < ApplicationController
def index
@things = Thing.ordered_by_position_asc
+ @things = @things.where('position >= ?', params[:min_position]) if params[:min_position]
end
def move
diff --git a/spec/dummy-4.2/app/controllers/things_controller.rb b/spec/dummy-4.2/app/controllers/things_controller.rb
index fa691f9..6f190f4 100644
--- a/spec/dummy-4.2/app/controllers/things_controller.rb
+++ b/spec/dummy-4.2/app/controllers/things_controller.rb
@@ -1,6 +1,7 @@
class ThingsController < ApplicationController
def index
@things = Thing.ordered_by_position_asc
+ @things = @things.where('position >= ?', params[:min_position]) if params[:min_position]
end
def move
diff --git a/spec/features/drag_and_drop_spec.rb b/spec/features/drag_and_drop_spec.rb
index be1d500..c8d9fab 100644
--- a/spec/features/drag_and_drop_spec.rb
+++ b/spec/features/drag_and_drop_spec.rb
@@ -4,78 +4,154 @@
context 'existing things' do
before(:each) { Thing.delete_all }
- let!(:thing1) { Thing.create }
- let!(:thing2) { Thing.create }
+ context 'all things at one page' do
+ let!(:thing1) { Thing.create! }
+ let!(:thing2) { Thing.create! }
+
+ describe 'drag up' do
+ let(:thing1_selector) { "li[data-role=thing#{thing1.id}]" }
+ let(:thing2_selector) { "li[data-role=thing#{thing2.id}]" }
+
+ subject do
+ visit '/'
+ page.execute_script "
+ $.getScript('/assets/jquery.simulate.js', function(){
+ var draggable = $('#{thing1_selector}');
+ var droppable = $('#{thing2_selector}');
+ var dy = droppable.offset().top - draggable.offset().top - 1;
+
+ draggable.simulate('drag', {dx:0, dy: dy});
+ });
+ "
+ sleep 3
+
+ click_button 'Refresh'
+ end
+
+ it 'change first thing position to 0' do
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing1.id}' and @data-position='0']")
+ end
+
+ it 'change second thing position to 1' do
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing2.id}' and @data-position='1']")
+ end
+ end
- describe 'drag up' do
- let(:thing1_selector) { "li[data-role=thing#{thing1.id}]" }
- let(:thing2_selector) { "li[data-role=thing#{thing2.id}]" }
+ describe 'drag down' do
+ let!(:thing1) { Thing.create! }
+ let!(:thing2) { Thing.create! }
- subject do
- sleep 3
- page.execute_script "
- $.getScript('/assets/jquery.simulate.js', function(){
- var draggable = $('#{thing1_selector}');
- var droppable = $('#{thing2_selector}');
- var dy = droppable.offset().top - draggable.offset().top - 1;
+ let(:thing1_selector) { "li[data-role=thing#{thing1.id}]" }
+ let(:thing2_selector) { "li[data-role=thing#{thing2.id}]" }
- draggable.simulate('drag', {dx:0, dy: dy});
- });
- "
- sleep 3
+ subject do
+ sleep 3
- click_button 'Refresh'
- end
+ delta = thing2.sortable_append ? -1 : 1
+ page.execute_script "
+ $.getScript('/assets/jquery.simulate.js', function(){
+ var draggable = $('#{thing2_selector}');
+ var droppable = $('#{thing1_selector}');
- it 'change first thing position to 0' do
- visit '/'
- subject
- expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing1.id}' and @data-position='0']")
- end
+ var dy = droppable.offset().top - draggable.offset().top + #{delta};
- it 'change second thing position to 1' do
- visit '/'
- subject
- expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing2.id}' and @data-position='1']")
+ draggable.simulate('drag', {dx:0, dy: dy});
+ });
+ "
+ sleep 3
+
+ click_button 'Refresh'
+ end
+
+ it 'change first thing position to 0' do
+ visit '/'
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing1.id}' and @data-position='#{thing1.sortable_append ? 1 : 0}']")
+ end
+
+ it 'change second thing position to 1' do
+ visit '/'
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing2.id}' and @data-position='#{thing2.sortable_append ? 0 : 1}']")
+ end
end
end
- describe 'drag down' do
- let!(:thing1) { Thing.create }
- let!(:thing2) { Thing.create }
+ # In some cases first thing position may be greater than 0 (pagination, for example)
+ context 'second page' do
+ describe 'drag up' do
+ let!(:thing0) { Thing.create! }
+ let!(:thing1) { Thing.create! }
+ let!(:thing2) { Thing.create! }
+
+ let(:thing1_selector) { "li[data-role=thing#{thing1.id}]" }
+ let(:thing2_selector) { "li[data-role=thing#{thing2.id}]" }
+
+ subject do
+ visit '/?min_position=1'
+ page.execute_script "
+ $.getScript('/assets/jquery.simulate.js', function(){
+ var draggable = $('#{thing1_selector}');
+ var droppable = $('#{thing2_selector}');
+ var dy = droppable.offset().top - draggable.offset().top - 1;
+
+ draggable.simulate('drag', {dx:0, dy: dy});
+ });
+ "
+ sleep 3
+
+ click_button 'Refresh'
+ end
+
+ it 'change first thing position to 1' do
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing1.id}' and @data-position='1']")
+ end
+
+ it 'change second thing position to 2' do
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing2.id}' and @data-position='2']")
+ end
+ end
- let(:thing1_selector) { "li[data-role=thing#{thing1.id}]" }
- let(:thing2_selector) { "li[data-role=thing#{thing2.id}]" }
+ describe 'drag down' do
+ let!(:thing0) { Thing.create! }
+ let!(:thing1) { Thing.create! }
+ let!(:thing2) { Thing.create! }
- subject do
- sleep 3
+ let(:thing1_selector) { "li[data-role=thing#{thing1.id}]" }
+ let(:thing2_selector) { "li[data-role=thing#{thing2.id}]" }
- delta = thing2.sortable_append ? -1 : 1
- page.execute_script "
- $.getScript('/assets/jquery.simulate.js', function(){
- var draggable = $('#{thing2_selector}');
- var droppable = $('#{thing1_selector}');
+ subject do
+ visit '/?min_position=1'
- var dy = droppable.offset().top - draggable.offset().top + #{delta};
+ delta = thing2.sortable_append ? -1 : 1
+ page.execute_script "
+ $.getScript('/assets/jquery.simulate.js', function(){
+ var draggable = $('#{thing2_selector}');
+ var droppable = $('#{thing1_selector}');
- draggable.simulate('drag', {dx:0, dy: dy});
- });
- "
- sleep 3
+ var dy = droppable.offset().top - draggable.offset().top + #{delta};
- click_button 'Refresh'
- end
+ draggable.simulate('drag', {dx:0, dy: dy});
+ });
+ "
+ sleep 3
- it 'change first thing position to 0' do
- visit '/'
- subject
- expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing1.id}' and @data-position='#{thing1.sortable_append ? 1 : 0}']")
- end
+ click_button 'Refresh'
+ end
- it 'change second thing position to 1' do
- visit '/'
- subject
- expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing2.id}' and @data-position='#{thing2.sortable_append ? 0 : 1}']")
+ it 'change first thing position to 1' do
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing1.id}' and @data-position='#{thing1.sortable_append ? 2 : 1}']")
+ end
+
+ it 'change second thing position to 2' do
+ subject
+ expect(page).to have_selector(:xpath, "//li[@data-role='thing#{thing2.id}' and @data-position='#{thing2.sortable_append ? 1 : 2}']")
+ end
end
end
end
@@ -88,7 +164,7 @@
let(:child2_selector) { "li[data-position='0']" }
subject do
- sleep 3
+ visit new_parent_path
page.execute_script %{
$.getScript('/assets/jquery.simulate.js', function(){
var draggable = $("#{child1_selector}");
@@ -104,13 +180,11 @@
end
it 'change first child position to 2' do
- visit new_parent_path
subject
expect(page).to have_selector("li[data-position='1']", text: 'Child 0')
end
it 'change last child position to 0' do
- visit new_parent_path
subject
expect(page).to have_selector("li[data-position='0']", text: 'Child 2')
end
@@ -121,7 +195,7 @@
let(:child2_selector) { "li[data-position='0']" }
subject do
- sleep 3
+ visit new_parent_path
page.execute_script %{
$.getScript('/assets/jquery.simulate.js', function(){
var draggable = $("#{child2_selector}");
@@ -137,19 +211,16 @@
end
it 'change first child position to 2' do
- visit new_parent_path
subject
expect(page).to have_selector("li[data-position='2']", text: 'Child 0')
end
it 'change middle child position to 0' do
- visit new_parent_path
subject
expect(page).to have_selector("li[data-position='0']", text: 'Child 1')
end
it 'change last child position to 1' do
- visit new_parent_path
subject
expect(page).to have_selector("li[data-position='1']", text: 'Child 2')
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 91cc7e2..1ab05ee 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -11,6 +11,7 @@
require 'capybara/rspec'
require 'capybara/rails'
require 'capybara/poltergeist'
+require 'phantomjs/poltergeist'
Capybara.javascript_driver = :poltergeist
Capybara.app = Dummy::Application