shuber-sortable 1.0.4 → 1.0.5

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.
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ 2009-05-30 - Sean Huber (shuber@huberry.com)
2
+ * Always convert options[:conditions] into an array
3
+ * options[:conditions] can be a hash
4
+ * Add higher_items and lower_items instance methods
5
+ * Update README.rdoc
6
+ * Update gemspec
7
+
8
+ 2009-05-29 - Sean Huber (shuber@huberry.com)
9
+ * Call remove_from_lists after destroy instead of before in case any other before_destroy callbacks fail
10
+
1
11
  2009-05-28 - Sean Huber (shuber@huberry.com)
2
12
  * All active record versions >= 2.0.0 are now fully supported
3
13
 
data/README.rdoc CHANGED
@@ -133,6 +133,9 @@ Any attributes specified as a :scope that are changed on an item cause the item
133
133
  # Returns a boolean after determining if the current item is the first item in the specified list
134
134
  first_item?(list_name = nil)
135
135
 
136
+ # Returns an array of items higher than the current item in the specified list
137
+ higher_items(list_name = nil)
138
+
136
139
  # Returns a boolean after determining if the current item is in the specified list
137
140
  in_list?(list_name = nil)
138
141
 
@@ -165,6 +168,9 @@ Any attributes specified as a :scope that are changed on an item cause the item
165
168
  # Returns 0 if there are no items in the specified list
166
169
  last_position(list_name = nil)
167
170
 
171
+ # Returns an array of items lower than the current item in the specified list
172
+ lower_items(list_name = nil)
173
+
168
174
  # Moves the current item down one position in the specified list and saves
169
175
  move_down!(list_name = nil)
170
176
 
data/lib/sortable.rb CHANGED
@@ -12,7 +12,7 @@ module Huberry
12
12
  # Accepts four options:
13
13
  #
14
14
  # :column => The name of the column that will be used to store an item's position in the list. Defaults to :position
15
- # :conditions => Any extra constraints to use if you need to specify a tighter scope than just a foreign key. Defaults to '1 = 1'
15
+ # :conditions => Any extra constraints to use if you need to specify a tighter scope than just a foreign key. Defaults to {}
16
16
  # :list_name => The name of the list (this is used when calling all sortable related instance methods). Defaults to nil
17
17
  # :scope => A foreign key or an array of foreign keys to use as list constraints. Defaults to []
18
18
  #
@@ -123,11 +123,22 @@ module Huberry
123
123
 
124
124
  define_attribute_methods
125
125
 
126
- options = { :column => :position, :conditions => '1 = 1', :list_name => nil, :scope => [] }.merge(options)
127
- options[:scope] = [options[:scope]] unless options[:scope].is_a?(Array)
126
+ options = { :column => :position, :conditions => {}, :list_name => nil, :scope => [] }.merge(options)
128
127
 
128
+ options[:conditions] = options[:conditions].inject(['1 = 1']) do |conditions, (key, value)|
129
+ conditions.first << " AND #{key.is_a?(Symbol) ? "#{table_name}.#{key}" : key} "
130
+ if value.nil?
131
+ conditions.first << 'IS NULL'
132
+ else
133
+ conditions.first << '= ?'
134
+ conditions << value
135
+ end
136
+ end if options[:conditions].is_a?(Hash)
137
+ options[:conditions] = Array(options[:conditions])
138
+
139
+ options[:scope] = Array(options[:scope])
129
140
  options[:scope].each do |scope|
130
- (options[:conditions].is_a?(Array) ? options[:conditions].first : options[:conditions]) << " AND (#{table_name}.#{scope} = ?) "
141
+ options[:conditions].first << " AND (#{table_name}.#{scope} = ?)"
131
142
 
132
143
  unless instance_methods.include?("#{scope}_with_sortable=")
133
144
  define_method "#{scope}_with_sortable=" do |value|
@@ -145,8 +156,8 @@ module Huberry
145
156
  def self.included(base)
146
157
  base.class_eval do
147
158
  before_create :add_to_lists
148
- before_destroy :remove_from_lists
149
159
  before_update :update_lists
160
+ after_destroy :remove_from_lists
150
161
  alias_method_chain :reload, :sortable
151
162
  end
152
163
  end
@@ -171,6 +182,14 @@ module Huberry
171
182
  self == first_item(list_name)
172
183
  end
173
184
 
185
+ # Returns an array of items higher than the current item in the specified list
186
+ def higher_items(list_name = nil)
187
+ options = evaluate_sortable_options(list_name)
188
+ options[:conditions].first << " AND #{self.class.table_name}.#{options[:column]} < ?"
189
+ options[:conditions] << send(options[:column])
190
+ self.class.base_class.find(:all, :conditions => options[:conditions], :order => options[:column])
191
+ end
192
+
174
193
  # Returns a boolean after determining if the current item is in the specified list
175
194
  def in_list?(list_name = nil)
176
195
  !new_record? && !send(evaluate_sortable_options(list_name)[:column]).nil?
@@ -213,9 +232,9 @@ module Huberry
213
232
  # Returns the last item in a list associated with the current item
214
233
  def last_item(list_name = nil)
215
234
  options = evaluate_sortable_options(list_name)
216
- (options[:conditions].is_a?(Array) ? options[:conditions].first : options[:conditions]) << " AND #{self.class.table_name}.#{options[:column]} IS NOT NULL "
235
+ options[:conditions].first << " AND #{self.class.table_name}.#{options[:column]} IS NOT NULL"
217
236
  klass, conditions = [self.class.base_class, { :conditions => options[:conditions] }]
218
- klass.send("find_by_#{options[:column]}".to_sym, klass.maximum(options[:column].to_s, conditions), conditions)
237
+ klass.send("find_by_#{options[:column]}".to_sym, klass.maximum(options[:column], conditions), conditions)
219
238
  end
220
239
 
221
240
  # Returns a boolean after determining if the current item is the last item in the specified list
@@ -231,6 +250,14 @@ module Huberry
231
250
  item.nil? ? 0 : item.send(evaluate_sortable_options(list_name)[:column])
232
251
  end
233
252
 
253
+ # Returns an array of items lower than the current item in the specified list
254
+ def lower_items(list_name = nil)
255
+ options = evaluate_sortable_options(list_name)
256
+ options[:conditions].first << " AND #{self.class.table_name}.#{options[:column]} > ?"
257
+ options[:conditions] << send(options[:column])
258
+ self.class.base_class.find(:all, :conditions => options[:conditions], :order => "#{self.class.table_name}.#{options[:column]}")
259
+ end
260
+
234
261
  # Moves the current item down one position in the specified list and saves
235
262
  def move_down!(list_name = nil)
236
263
  in_list?(list_name) && (last_item?(list_name) || insert_at!(send(evaluate_sortable_options(list_name)[:column]) + 1, list_name))
@@ -306,13 +333,12 @@ module Huberry
306
333
  # Returns the evaluated options
307
334
  def evaluate_sortable_options(list_name = nil)
308
335
  self.class.assert_sortable_list_exists!(list_name)
309
- options = self.class.sortable_lists[list_name.to_s].inject({}) { |hash, pair| hash.merge! pair.first => (pair.last.dup rescue pair.last) }
336
+ options = self.class.sortable_lists[list_name.to_s].inject({}) { |hash, (key, value)| hash.merge! key => Marshal::load(Marshal.dump(value)) } # deep dup
310
337
  options[:scope].each do |scope|
311
338
  value = send(scope)
312
339
  if value.nil?
313
- (options[:conditions].is_a?(Array) ? options[:conditions].first : options[:conditions]).gsub!(/#{scope} \= \?/, "#{scope} IS NULL")
340
+ options[:conditions].first.gsub!(/#{scope} \= \?/, "#{scope} IS NULL")
314
341
  else
315
- options[:conditions] = [options[:conditions]] unless options[:conditions].is_a?(Array)
316
342
  options[:conditions] << value
317
343
  end
318
344
  end
@@ -323,7 +349,8 @@ module Huberry
323
349
  # <tt>direction</tt> (:up or :down) for the specified list
324
350
  def move_lower_items(direction, position, list_name = nil)
325
351
  options = evaluate_sortable_options(list_name)
326
- (options[:conditions].is_a?(Array) ? options[:conditions].first : options[:conditions]) << " AND #{self.class.table_name}.#{options[:column]} > '#{position}' AND #{self.class.table_name}.#{options[:column]} IS NOT NULL "
352
+ options[:conditions].first << " AND #{self.class.table_name}.#{options[:column]} > ? AND #{self.class.table_name}.#{options[:column]} IS NOT NULL"
353
+ options[:conditions] << position
327
354
  self.class.base_class.update_all "#{options[:column]} = #{options[:column]} #{direction == :up ? '-' : '+'} 1", options[:conditions]
328
355
  end
329
356
 
@@ -331,7 +358,7 @@ module Huberry
331
358
  def remove_from_list(list_name = nil)
332
359
  options = evaluate_sortable_options(list_name)
333
360
  move_lower_items(:up, send(options[:column]), list_name)
334
- send("#{options[:column]}=".to_sym, nil)
361
+ send("#{options[:column]}=".to_sym, nil) unless self.frozen?
335
362
  end
336
363
 
337
364
  # Removes the current item from all sortable lists
@@ -24,7 +24,9 @@ def create_tables
24
24
 
25
25
  create_table :users do |t|
26
26
  t.string :type
27
+ t.string :name
27
28
  t.integer :position
29
+ t.integer :steves_position
28
30
  end
29
31
  end
30
32
  end
@@ -44,6 +46,7 @@ end
44
46
 
45
47
  class User < ActiveRecord::Base
46
48
  sortable :scope => :type
49
+ sortable :conditions => { :name => 'steve' }, :column => :steves_position, :list_name => :steves
47
50
  end
48
51
 
49
52
  class Admin < User
@@ -297,4 +300,27 @@ class SortableTest < Test::Unit::TestCase
297
300
  assert_equal 2, @admin_2.position
298
301
  end
299
302
 
303
+ def test_should_accept_hash_conditions
304
+ @user = User.create :name => 'steve'
305
+ @user_2 = User.create :name => 'bob'
306
+ @user_3 = User.create :name => 'steve'
307
+ assert_equal 1, @user.steves_position
308
+ assert_equal 2, @user_2.steves_position
309
+ assert_equal 2, @user_3.steves_position
310
+ end
311
+
312
+ def test_should_return_higher_items
313
+ @user = User.create
314
+ @user_2 = User.create
315
+ @user_3 = User.create
316
+ assert_equal [@user, @user_2], @user_3.higher_items
317
+ end
318
+
319
+ def test_should_return_lower_items
320
+ @user = User.create
321
+ @user_2 = User.create
322
+ @user_3 = User.create
323
+ assert_equal [@user_2, @user_3], @user.lower_items
324
+ end
325
+
300
326
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shuber-sortable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Huber
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-28 00:00:00 -07:00
12
+ date: 2009-05-30 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15