cw_sort_order 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/lib/cw_sort_order.rb +347 -0
- metadata +66 -0
data/.gemtest
ADDED
File without changes
|
@@ -0,0 +1,347 @@
|
|
1
|
+
# Copyright 2011 Christopher Wojno
|
2
|
+
|
3
|
+
module CW
|
4
|
+
|
5
|
+
# Specifies Sorting Order
|
6
|
+
#
|
7
|
+
# Provides a clean way to express how columns are to be sorted. It also lets you build them in your models and re-use the ordering without having to put them into controllers so you can use them across many controllers. Or you can stick them in controllers and use them across actions. Or you can YAMLize them and put them into databases... Why? I dunno, sounds like a cool idea.
|
8
|
+
#
|
9
|
+
# So, this class doesn't actually DO any sorting. It merely represents a cascading sorting order. Think of it like an ORDER BY clause in a SQL Query. It doesn't actually sort, but tells whatever classes that use it how to sort.
|
10
|
+
#
|
11
|
+
# Example, say you had a users table with fields: first, last, age. If you want to order by last name, then first name, then oldest to youngest, create an order thusly:
|
12
|
+
#
|
13
|
+
# CW::SortOrder.new.asc('last').asc('first').desc('age')
|
14
|
+
#
|
15
|
+
# OR
|
16
|
+
#
|
17
|
+
# CW::SortOrder.up('last').up('first').dn('age')
|
18
|
+
#
|
19
|
+
# OR
|
20
|
+
#
|
21
|
+
# CW::SortOrder.new + 'last' + 'first' - 'age'
|
22
|
+
class SortOrder
|
23
|
+
attr_reader :ordering
|
24
|
+
|
25
|
+
# Creates a new SortOrder, with nothing sorted
|
26
|
+
def initialize
|
27
|
+
@ordering = []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Ascending Constructor
|
31
|
+
#
|
32
|
+
# Creates a new SortOrder class with an ascending sort on the given column
|
33
|
+
#
|
34
|
+
# Arguments:
|
35
|
+
# column_name: (String) representing the field or column upon which to perform the order
|
36
|
+
def self.asc( column_name )
|
37
|
+
self.new.asc( column_name )
|
38
|
+
end
|
39
|
+
class << self
|
40
|
+
alias :up :asc
|
41
|
+
end
|
42
|
+
|
43
|
+
# Descending Constructor
|
44
|
+
#
|
45
|
+
# Creates a new SortOrder class with an descending sort on the given column
|
46
|
+
#
|
47
|
+
# Arguments:
|
48
|
+
# column_name: (String) representing the field or column upon which to perform the order
|
49
|
+
def self.desc( column_name )
|
50
|
+
self.new.desc( column_name )
|
51
|
+
end
|
52
|
+
class << self
|
53
|
+
alias :down :desc
|
54
|
+
alias :dn :desc
|
55
|
+
end
|
56
|
+
|
57
|
+
# Append Ascending Column
|
58
|
+
#
|
59
|
+
# Appends (adds) a sorting order to an existing set of sorting orders. The appended order is ascending.
|
60
|
+
#
|
61
|
+
# Arguments:
|
62
|
+
# column_name: (String) representing the field or column upon which to perform the order
|
63
|
+
#
|
64
|
+
# Returns:
|
65
|
+
# self: (CW::SortOrder)
|
66
|
+
def asc( column_name )
|
67
|
+
push( column_name, :asc )
|
68
|
+
end
|
69
|
+
alias :up :asc
|
70
|
+
|
71
|
+
# Append Descending Column
|
72
|
+
#
|
73
|
+
# Appends (adds) a sorting order to an existing set of sorting orders. The appended order is descending.
|
74
|
+
#
|
75
|
+
# Arguments:
|
76
|
+
# column_name: (String) representing the field or column upon which to perform the order
|
77
|
+
#
|
78
|
+
# Returns:
|
79
|
+
# self: (CW::SortOrder)
|
80
|
+
def desc( column_name )
|
81
|
+
push( column_name, :desc )
|
82
|
+
end
|
83
|
+
alias :down :desc
|
84
|
+
alias :dn :desc
|
85
|
+
|
86
|
+
# Append Descending Column
|
87
|
+
#
|
88
|
+
# Appends (adds) a sorting order to an existing set of sorting orders. The appended order is descending.
|
89
|
+
#
|
90
|
+
# Arguments:
|
91
|
+
# column_name: (String) representing the field or column upon which to perform the order
|
92
|
+
#
|
93
|
+
# Returns:
|
94
|
+
# self: (CW::SortOrder)
|
95
|
+
def -( column_name )
|
96
|
+
desc( column_name )
|
97
|
+
end
|
98
|
+
|
99
|
+
# Append Ascending Column
|
100
|
+
#
|
101
|
+
# Appends (adds) a sorting order to an existing set of sorting orders. The appended order is ascending.
|
102
|
+
#
|
103
|
+
# Arguments:
|
104
|
+
# column_name: (String) representing the field or column upon which to perform the order
|
105
|
+
#
|
106
|
+
# Returns:
|
107
|
+
# self: (CW::SortOrder)
|
108
|
+
def +( column_name )
|
109
|
+
asc( column_name )
|
110
|
+
end
|
111
|
+
|
112
|
+
# Push
|
113
|
+
#
|
114
|
+
# Appends a sorting order very literally. This method is NOT safe and is for internal use (or child class use) only.
|
115
|
+
#
|
116
|
+
# Arguments:
|
117
|
+
# column_name: (String) representing the field or column upon which to perform the order
|
118
|
+
# direction: (Symbol) [:asc|:desc] representing the direction to sort the given column_name. :desc to sort descending, :asc to sort ascending.
|
119
|
+
#
|
120
|
+
# Returns:
|
121
|
+
# self: (CW::SortOrder)
|
122
|
+
def push( column_name, direction )
|
123
|
+
@ordering << { column_name => direction }
|
124
|
+
self
|
125
|
+
end
|
126
|
+
protected :push
|
127
|
+
|
128
|
+
# Size
|
129
|
+
#
|
130
|
+
# Returns how many sorting orders there are (columns).
|
131
|
+
#
|
132
|
+
# Returns:
|
133
|
+
# (Integer) The number of columns represented. Note, if you add the same column twice, this class is NOT smart enough to figure it out.
|
134
|
+
def size
|
135
|
+
ordering.size
|
136
|
+
end
|
137
|
+
|
138
|
+
# Accessor
|
139
|
+
#
|
140
|
+
# Gets an ordering pair of type: [column_name,:direction]
|
141
|
+
#
|
142
|
+
# Arguments:
|
143
|
+
# i: (Integer) the index of the ordering pair. Should be 0..(size - 1)
|
144
|
+
#
|
145
|
+
# Returns:
|
146
|
+
# The ordering pair as a 2-element array. The first element is the column name, the second is either :asc or :desc representing ascending and descending sort orders, respectively.
|
147
|
+
def [](i)
|
148
|
+
ordering[i]
|
149
|
+
end
|
150
|
+
|
151
|
+
# Column Name At
|
152
|
+
#
|
153
|
+
# Gets the name of the column at the index position given
|
154
|
+
#
|
155
|
+
# Arguments:
|
156
|
+
# i: (Integer) the index of the ordering pair. Should be 0..(size - 1)
|
157
|
+
#
|
158
|
+
# Returns:
|
159
|
+
# The column name at the index provided.
|
160
|
+
def at(i)
|
161
|
+
ordering[i].first
|
162
|
+
end
|
163
|
+
|
164
|
+
# Flip Sort Order
|
165
|
+
#
|
166
|
+
# Causes the indicated column to be sorted in the opposite direction
|
167
|
+
#
|
168
|
+
# Arguments:
|
169
|
+
# name_or_index: (String|Integer) The column name or the index of the column to flip the sort direction (order)
|
170
|
+
#
|
171
|
+
# Returns:
|
172
|
+
# self
|
173
|
+
def flip(name_or_index)
|
174
|
+
i = name_or_index
|
175
|
+
col_name = name_or_index
|
176
|
+
# given string
|
177
|
+
if name_or_index.is_a?(String)
|
178
|
+
i = columns.index( name_or_index )
|
179
|
+
# given int
|
180
|
+
else
|
181
|
+
col_name = ordering[i].first.first
|
182
|
+
end
|
183
|
+
h = ordering[i]
|
184
|
+
if h[col_name].eql?(:desc)
|
185
|
+
h[col_name] = :asc
|
186
|
+
else
|
187
|
+
h[col_name] = :desc
|
188
|
+
end
|
189
|
+
@ordering[i] = h
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
# Each
|
194
|
+
#
|
195
|
+
# Iterates over each ordering constraint.
|
196
|
+
#
|
197
|
+
# Block:
|
198
|
+
# Arguments:
|
199
|
+
# column_name: (String) the column to be sorted
|
200
|
+
# direction: (Symbol) :asc for ascending sort, :desc for descending sort
|
201
|
+
#
|
202
|
+
def each
|
203
|
+
ordering.each do |r|
|
204
|
+
yield r.first.first, r.first.last
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Collect
|
209
|
+
#
|
210
|
+
# Collects over each ordering constraint.
|
211
|
+
#
|
212
|
+
# Block:
|
213
|
+
# Arguments:
|
214
|
+
# column_name: (String) the column to be sorted
|
215
|
+
# direction: (Symbol) :asc for ascending sort, :desc for descending sort
|
216
|
+
#
|
217
|
+
def collect
|
218
|
+
ordering.collect do |r|
|
219
|
+
yield r.first.first, r.first.last
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Columns
|
224
|
+
#
|
225
|
+
# Gets an array of all column names. The order is preserved.
|
226
|
+
#
|
227
|
+
# Returns:
|
228
|
+
# (Array) of column names
|
229
|
+
def columns
|
230
|
+
ordering.collect{|o| o.first.first}
|
231
|
+
end
|
232
|
+
|
233
|
+
# Directions
|
234
|
+
#
|
235
|
+
# Gets an array of all directions. The order is preserved.
|
236
|
+
#
|
237
|
+
# Returns:
|
238
|
+
# (Array) of direction symbols (:asc/:desc)
|
239
|
+
def directions
|
240
|
+
ordering.collect{|o| o.first.last}
|
241
|
+
end
|
242
|
+
|
243
|
+
# Reverse
|
244
|
+
#
|
245
|
+
# Given an existing SortOrder, will reverse the columns and respective sort order. Therefore, if you have:
|
246
|
+
#
|
247
|
+
# > (CW::SortOrder.new + '1' - '2' - '3' - '4').reverse
|
248
|
+
# => #<CW::SortOrder:0x10033db68 @ordering=[{"4"=>:desc}, {"3"=>:desc}, {"2"=>:desc}, {"1"=>:asc}]>
|
249
|
+
#
|
250
|
+
# I'm not sure how this is useful. Whatever.
|
251
|
+
#
|
252
|
+
# Returns:
|
253
|
+
# A copy of the caller, but with all columns and respective orderings reversed
|
254
|
+
def reverse
|
255
|
+
o = SortOrder.new
|
256
|
+
ordering.reverse.each do |r|
|
257
|
+
o.push( r.first.first, r.first.last )
|
258
|
+
end
|
259
|
+
o
|
260
|
+
end
|
261
|
+
|
262
|
+
# Invert
|
263
|
+
#
|
264
|
+
# Given an existing SortOrder, will INVERT the columns and respective sort order. The columns do not move, but their orderings will be flipped Therefore, if you have:
|
265
|
+
#
|
266
|
+
# > (CW::SortOrder.new + '1' - '2' - '3' - '4').invert
|
267
|
+
# => #<CW::SortOrder:0x10032f428 @ordering=[{"1"=>:desc}, {"2"=>:asc}, {"3"=>:asc}, {"4"=>:asc}]>
|
268
|
+
#
|
269
|
+
# Returns:
|
270
|
+
# A copy of the caller, but with all column orderings inverted
|
271
|
+
def invert
|
272
|
+
o = self.dup
|
273
|
+
0.upto(o.size - 1) do |i|
|
274
|
+
o.flip(i)
|
275
|
+
end
|
276
|
+
o
|
277
|
+
end
|
278
|
+
|
279
|
+
# Shift
|
280
|
+
#
|
281
|
+
# Pops the first order from the front and returns it as an array ['column_name',:asc|:desc]
|
282
|
+
#
|
283
|
+
# This was useful when creating the cw_pagination gem as it let me pull off the last element (of a reversed ordering) and manipulated it outside of a loop
|
284
|
+
#
|
285
|
+
# Returns:
|
286
|
+
# (Array) an ordered pair: [column_name,:asc|:desc]
|
287
|
+
def shift
|
288
|
+
@ordering.shift.first
|
289
|
+
end
|
290
|
+
|
291
|
+
# Select
|
292
|
+
#
|
293
|
+
# Just like any Enumerable
|
294
|
+
#
|
295
|
+
# Arguments:
|
296
|
+
# column_name: (String) the column to be sorted
|
297
|
+
# direction: (Symbol) :asc for ascending sort, :desc for descending sort
|
298
|
+
#
|
299
|
+
# Returns:
|
300
|
+
# (CW::SortOrder) but only containing the selected orderings
|
301
|
+
def select
|
302
|
+
o = SortOrder.new
|
303
|
+
ordering.each do |r|
|
304
|
+
c = r.first.first
|
305
|
+
d = r.first.last
|
306
|
+
o.push( c, d ) if yield( c, d )
|
307
|
+
end
|
308
|
+
o
|
309
|
+
end
|
310
|
+
|
311
|
+
# Reject
|
312
|
+
#
|
313
|
+
# Just like any Enumerable
|
314
|
+
#
|
315
|
+
# Arguments:
|
316
|
+
# column_name: (String) the column to be sorted
|
317
|
+
# direction: (Symbol) :asc for ascending sort, :desc for descending sort
|
318
|
+
#
|
319
|
+
# Returns:
|
320
|
+
# (CW::SortOrder) but without the rejected orderings
|
321
|
+
def reject
|
322
|
+
o = SortOrder.new
|
323
|
+
ordering.each do |r|
|
324
|
+
c = r.first.first
|
325
|
+
d = r.first.last
|
326
|
+
o.push( c, d ) unless yield( c, d )
|
327
|
+
end
|
328
|
+
o
|
329
|
+
end
|
330
|
+
|
331
|
+
# Concatenate
|
332
|
+
#
|
333
|
+
# Combines two CW::SortOrders to form a new one that is the union of the caller and the argument.
|
334
|
+
#
|
335
|
+
# Example:
|
336
|
+
# > (CW::SortOrder.new + '1' - '2' - '3' - '4').concat( CW::SortOrder.new - '5' + '6' + '7' )
|
337
|
+
# => #<CW::SortOrder:0x10031a6e0 @ordering=[{"1"=>:asc}, {"2"=>:desc}, {"3"=>:desc}, {"4"=>:desc}, {"5"=>:desc}, {"6"=>:asc}, {"7"=>:asc}]>
|
338
|
+
#
|
339
|
+
# Returns:
|
340
|
+
# A new CW::SortOrder, c such that for CW::SortOrders a and b, c = a || b OR c = a.concat(b). This also lets you chain up sort orders
|
341
|
+
def concat( o )
|
342
|
+
raise ArgumentError.new( "Argument is not a CW::SortOrder" ) unless o.is_a?(CW::SortOrder)
|
343
|
+
@ordering.concat(o.ordering)
|
344
|
+
self
|
345
|
+
end
|
346
|
+
end # SortOrder
|
347
|
+
end # CW
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cw_sort_order
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Christopher Wojno
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-10 00:00:00 Z
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: A construct for representing the sorting order of a set of fields which can be sorted in a cascading fasion. Originally designed for Rails 2.3.14
|
22
|
+
email: cw_order.gem@wojnosystems.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files: []
|
28
|
+
|
29
|
+
files:
|
30
|
+
- lib/cw_sort_order.rb
|
31
|
+
- .gemtest
|
32
|
+
homepage: http://www.wojnosystems.com
|
33
|
+
licenses: []
|
34
|
+
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
hash: 3
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.10
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Sort Order construct
|
65
|
+
test_files: []
|
66
|
+
|