deepj / active-record-florder

Floating point ActiveRecord model ordering for rich client apps.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ActiveRecordFlorder

Build Status Code Climate Test Coverage Inline docs Gem Version

Floating point ActiveRecord Models ordering for rich client apps heavily inspirated by Trello's ordering alorithm. ActiveRecordFlorder let client decide model's position in collection, normalize given value and resolve conflicts to keep your data clean. It's highly optimalized and generate as small SQL queries. The whole philosophy is to load and update as little records as possible so in 99% it runs just one SELECT and one UPDATE. In edge cases sanitization of all records happens and bring records back to the "Garden of Eden" state. It's implemented with both Rails and non-Rails apps in mind and highly configurable.

Installation

add to your Gemfile if you're using Bundler

gem 'active_record_florder', '~> 0.1.0'

or simply install via Ruby Gems

gem install active_record_florder

Api

This gem defines new method for ActiveRecord::Base named florder.

Parameters

  • direction {Symbol} values: :asc :desc, required
  • options {Hash}, optional

Options

  • scope {Symbol} - ordering scope (should be relationship or any other model property)

  • attribute {Sumbol} - position column name, default: position

  • min_delta {Number} - Minimal allowed position delata, affect position normalization *

  • step {Number} - Optimal (init) delta between positions *

  • return_all_affected {Boolean} - If true move method will return array of all affected records instead of just self

  • Setting this should affect performance. We recommend using default values

Example

class Post < ActiveRecord::Base
  florder :desc, scope: :user, attribute: :order_position, min_delta: 0.001, step: 2**8, return_all_affected: true
end

Usage

If you're using Rails or ActiveRecordMigrations create new migration:

class AddPositionToPosts < ActiveRecord::Migration
  def change
    add_column :posts, :position, :float, default: 0
  end
end

With rails you can use generator for this:

rails g migration add_position_to_posts position:float

Now migrate your database:

rake db:migrate

To use ActiveRecordFlorder add this line to your model:

class Post < ActiveRecord::Base
  florder :desc
end

Now you can start using it:

# Get ordered Posts
Post.ordered

# change position of first post
Post.first.move(123.123)

Migrating Existing Data

ActiveRecordFlorder adds new before save model hook create position for new records. To make this work correctly you will need to initialize positions for your existing data. ActiveRecordFlorder provides simple class method for this. You can define rake task or call it right inside your migration.

Posts.reinit_positions

This method also should be used for optimalizing or repairing positions. It will not affect order of records just generate optimal positions for current order.

ASCending vs DESCending ordering

The only required parameter for each model is :asc or :desc order param. Each of them is optimalised for one of the cases - creating new models as first or last. Basically created model has highest position value in collection. Because of this whole interval is increased with each new record. Negative values are not allowed so creating new models with lowest position will decrese number of possible positions and increase possibility in conflicts. This is why this is desibled by default.

Simply use one of this:

  • Use ASC ordering when you want to create new models as last
  • Use DESC ordering when you want to create new models as first

Front-End Implementation

Client itself request position for given Model. This Gem is build with drag and drop interfaces in first place in mind. Calculation on is simple - you just need to now position of two sibling to place model in middle of them. For first and last position simply use hiher/lower value than first / last.

Here is hypotetical implementation in JavaScript:

/*
 * Calculate new requested position for model
 * @param abovePosition {number}
 * @param belowPosition {number}
 * @returns {number}
 */
function calculateNewPosition(abovePosition, belowPosition) {
  if (!abovePosition) {
    return bellowPosition ? bellowPosition * 2 : false;
  } else if (!bellowPosition) {
    return abovePosition / 2;
  }

  return (bellowPosition + abovePosition) / 2;
}

Configuration

You can use initializer to overwrite default settings like this:

ActiveRecordFlorder.configure do |config|
  config.scope = :owner
  config.attribute = :position_2
  config.step = 3
  config.min_delta = 0.1
end

Upgrading

Please see summary of changes for each version in changelog

Developing

Git clone repository and cd in it:

git clone git@github.com:turboMaCk/active-record-florder.git
cd active-record-florder

Copy configuration:

cp db/config.example.yml db/config.yml

Install dependencies:

bundle install

Run tests:

bundle exec rspec

or

bundle exec rake spec

That's it! We can't wait for your PR!

License

MIT

About

Floating point ActiveRecord model ordering for rich client apps.

License:MIT License


Languages

Language:Ruby 100.0%