cw_sort_order 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/.gemtest +0 -0
  2. data/lib/cw_sort_order.rb +347 -0
  3. 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
+