Giter Site home page Giter Site logo

dm-is-list's Introduction

dm-is-list

DataMapper plugin for creating and organizing lists.

Installation

Stable

Install the dm-is-list gem.

$ (sudo)? gem install dm-is-list

Edge

Download or clone dm-is-list from Github.

$ cd /path/to/dm-is-list

$ rake install            # will install dm-is-list

# enter your password at the prompt, if required
$ password ...

Getting started

To start using this gem, just require dm-is-list in your app.

require 'dm-core'         # must be required first
require 'dm-is-list'

Lets say we have a User class, and we want to give users the possibility of having their own todo-lists.

class User
  include DataMapper::Resource

  property :id,   Serial
  property :name, String

  has n, :todos
end

class Todo
  include DataMapper::Resource

  property :id,    Serial
  property :title, String
  property :done,  DateTime

  belongs_to :user

  # here we define that this should be a list, scoped on :user_id
  is :list, :scope => :user_id  # you may also pass in multiple properties, eg [ :user_id, :title ]
end

Once we have our Users and Lists, we might want to work with…

Movements of list items

Any list item can be moved around within the same list easily through the #move method.

:move( vector )

There are number of convenient vectors that help you move items around within the list.

item = Todo.get(1)
other = Todo.get(2)

item.move(:highest)          # moves to top of list.
item.move(:lowest)           # moves to bottom of list.
item.move(:top)              # moves to top of list.
item.move(:bottom)           # moves to bottom of list.
item.move(:up)               # moves one up (:higher and :up is the same) within the scope.
item.move(:down)             # moves one up (:lower and :down is the same) within the scope.
item.move(:to => position)   # moves item to a specific position.
item.move(:above => other)   # moves item above the other item.*
item.move(:below => other)   # moves item above the other item.*

# * won't move if the other item is in another scope. (should this be enabled?)

The list will act as intelligently as possible and keep positions in a logical running order.

:move( Integer )

NOTE! VERY IMPORTANT!

If you set the position manually, and then save, the list will NOT reorganize itself.

item.position = 3      # setting position manually
item.save              # the item will now have position 3, but the list may have two items with the same position.

# alternatively
item.update(:position => 3)    # sets the position manually, but does not reorganize the list positions.

You should therefore always use the item.move(N) syntax instead.

item.move(3)          # does the same as above, but in one call AND *reorganizes* the list.

<hr>

Hold On!

dm-is-list used to work with item.position = 1 type syntax. Why this change?

The main reason behind this change was that the previous version of dm-is-list created a LOT of extra SQL queries in order to support the manual updating of position, and as a result had a quite a few bugs/issues, which have been fixed in this version.

The other reason is that I couldn’t work out how to keep the functionality without adding the extra queries. But perhaps you can ?

<hr>

See “Batch Changing Positions” below for information on how to change the positions on a whole list.

Movements between scopes

When you move items between scopes, the list will try to work with your intentions.

Move the item from list to new list and add the item to the bottom of that list.

item.user_id                 # => 1
item.move_to_list(10)        # => the scope id ie User.get(10).id

# results in...
item.user_id                 # => 10
item.position                # => < bottom of the list >

Move the item from list to new list and add at the position given.

item.user_id                 # => 1
item.move_to_list(10, 2)     # => the scope id ie User.get(10).id,  position => 2

# results in...
item.user_id                 # => 10
item.position                # => 2

Batch Changing Positions

A common scenario when working with lists is the sorting of a whole list via something like JQuery’s sortable() functionality.
(Think re-arranging the order of Todo’s according to priority or something similar)

Optimum scenario

The most SQL query efficient way of changing the positions is:

sort_order = [5,4,3,2,1]              # list from AJAX request..

items = Todo.all(:user => @u1)        # loads all 5 items in the list

items.each{ |item| item.update(:position => sort_order.index(item.id) + 1) }   # remember the +1 since array's are indexed from 0

The above code will result in something like these queries.

# SELECT "id", "title", "position", "user_id" FROM "todos" WHERE "user_id" = 1 ORDER BY "position"
# UPDATE "todos" SET "position" = 5 WHERE "id" = 1
# UPDATE "todos" SET "position" = 4 WHERE "id" = 2
# UPDATE "todos" SET "position" = 2 WHERE "id" = 4
# UPDATE "todos" SET "position" = 1 WHERE "id" = 5

Remember! Your sort order list has to be the same length as the found items in the list, or your loop will fail.

Wasteful scenario

You can also use this version, but it will create upto 5 times as many SQL queries. :(

sort_order = ['5','4','3','2','1']    # list from AJAX request..

items = Todo.all(:user => @u1)        # loads all 5 items in the list

items.each{ |item| item.move(sort_order.index(item.id).to_i + 1) }   # remember the +1 since array's are indexed from 0

The above code will result in something like these queries:

#  SELECT "id", "title", "position", "user_id" FROM "todos" WHERE "user_id" = 1 ORDER BY "position"

#  SELECT "id", "title", "position", "user_id" FROM "todos" WHERE "user_id" = 1 ORDER BY "position" DESC LIMIT 1
#  SELECT "id" FROM "todos" WHERE "user_id" = 1 AND "id" IN (1, 2, 3, 4, 5) AND "position" BETWEEN 1 AND 5 ORDER BY "position"
#  UPDATE "todos" SET "position" = "position" + -1 WHERE "user_id" = 1 AND "position" BETWEEN 1 AND 5
#  SELECT "id", "position" FROM "todos" WHERE "id" IN (1, 2, 3, 4, 5) ORDER BY "id"
#  UPDATE "todos" SET "position" = 5 WHERE "id" = 1

#  SELECT "id", "title", "position", "user_id" FROM "todos" WHERE "user_id" = 1 ORDER BY "position" DESC LIMIT 1
#  SELECT "id" FROM "todos" WHERE "user_id" = 1 AND "id" IN (1, 2, 3, 4, 5) AND "position" BETWEEN 1 AND 4 ORDER BY "position"
#  UPDATE "todos" SET "position" = "position" + -1 WHERE "user_id" = 1 AND "position" BETWEEN 1 AND 4
#  SELECT "id", "position" FROM "todos" WHERE "id" IN (2, 3, 4, 5) ORDER BY "id"
#  UPDATE "todos" SET "position" = 4 WHERE "id" = 2

#  ...

As you can see it will also do the job, but will be more expensive.

RTFM

As I said above, for a better understanding of this gem/plugin, make sure you study the ‘dm-is-list/spec/integration/list_spec.rb’ tests.

Errors / Bugs

If something is not behaving intuitively, it is a bug, and should be reported. Report it here: datamapper.lighthouseapp.com/

TODOs

  • None at the moment. Or do you think something is missing?

Note on Patches/Pull Requests

  • Fork the project.

  • Make your feature addition or bug fix.

  • Add tests for it. This is important so we don’t break it in a future version unintentionally.

  • Commit, do not mess with rakefile, version, or history.

    • (if you want to have your own version, that is fine but bump version in a commit by itself we can ignore when we pull)

  • Send us a pull request. Bonus points for topic branches.

Copyright © 2011 Sindre Aarsaether. Released under the MIT License.

See LICENSE for details.

Credits

Credit also goes to these contributors.

dm-is-list's People

Contributors

be9 avatar bernerdschaefer avatar dkubb avatar gix avatar kematzy avatar michaelklishin avatar myabc avatar namelessjon avatar paul avatar sam avatar snusnu avatar solnic avatar somebee avatar tpitale avatar xaviershay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

dm-is-list's Issues

`move :below` fails

test case: https://github.com/FND/dm-is-list/compare/below_bug

the first .move :below fails - yet it succeeds if first doing an arbitrary .move :above

Since the RSpec test case seems potentially confusing, here's a sample script that might make it clearer:

module DEBUG

  def self.info
    puts Animal.all(:order => :position.asc).map(&:id).join("  ")
  end

  def self.move(id, vector, ref_id)
    puts Animal.get!(id).move vector => Animal.get!(ref_id)
  end

end

5.times do |i|
  name = "animal#{i}"
  Animal.create
end

DEBUG::info # 1  2  3  4  5
DEBUG::move 5, :below, 1 # false
DEBUG::info # 1  2  3  4  5
DEBUG::move 5, :above, 1 # true
DEBUG::info # 5  1  2  3  4
DEBUG::move 5, :below, 1 # true
DEBUG::info # 1  5  2  3  4

Indexed position field

Since the position field will probably be constantly used for ordering, it might be nice to change the property definition to
property :position, Integer, :index => true

Saving Null Values

Greetings,

I have the following models, and I am using dm-is-list to sort them:

class Section
        include DataMapper::Resource

        property :id, Serial
        property :name, String, :length => 1..100
        property :credits, String, :length => 1..10

        has n, :courses, :through => Resource

        is :list, :scope => [:program_id]
    end

    class Course
        include DataMapper::Resource

        property :id, Serial
        property :subj, String, :length => 4
        property :code, String, :length => 3..5
        property :name, String, :length => 1..100
        property :description, Text
        property :credits, String, :length => 1..10

        has n, :sections, :through => Resource

    end

    class CourseSection
        include DataMapper::Resource

        belongs_to :course, :key => true
        belongs_to :section, :key => true

        is :list, :scope => [:section_id]
    end

I am unable to select a given course and move it, so I am moving the Course Sections. I can use CourseSection.move(position) without issue. My problem gets more complicated when I try to use move_to_list(sectionID, position).

dm-is-list is setting the position value to NULL (I have verified that position is being sent). I have even tried to use move_to_list(sectionID) with no position, and NULL is still saved to the database. Obviously, subsequent attempts to move the item after that fail.

I attempted to define the position property as not null but that just resulted in an error when it tried to save the null. Position should never be null. This is making the gem unusable for me at this time.

inherited class method does not exist

When I tried use dm-is-list with datamapper 1.3.0beta I error message with inherited hook.

Below I past part of necessary error message:

/.../gems/dm-core-f51603618654/lib/dm-core/support/hook.rb:169:in register_hook': inherited class method does not exist (ArgumentError) from /.../gems/dm-core-f51603618654/lib/dm-core/support/hook.rb:353:ininstall_hook'
from /.../gems/dm-core-f51603618654/lib/dm-core/support/hook.rb:72:in after_class_method' from /.../gems/1.9.1/bundler/gems/dm-is-list-6826cd2647bf/lib/dm-is-list/is/list.rb:297:inis_list'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.