listalicious 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document CHANGED
File without changes
data/.gitignore CHANGED
File without changes
data/LICENSE CHANGED
File without changes
@@ -4,9 +4,7 @@ Semantic listing; a semantic way to build datagrid structures in Rails.
4
4
 
5
5
  h2. The Story
6
6
 
7
- Ok, so I really liked what came of my navigasmic gem, and it's proved itself useful and flexible. I'm also working on unifying some aspects of a CMS and decided that datagrids should be unified to some degree so I can simplify the generators, make broad changes if I need to, and have a potentially more efficient way to create them.
8
-
9
- So I started this project to do just that. I quickly learned that datagrids aren't especially easy to make amazing, and there's a lot of things that one might want to do with them... grouping (with a row that creates a separator), sorting, ordering, additional informational rows, footers, ajax pagination, etc. etc. etc.. The list is actually pretty long, and I wrote a lot of those things, but it's nowhere near perfect. I sort of don't think it's possible with how I've approached it this time around. Anyhow, I thought I could get something to a point that simplified the datagrid process, and I believe I've succeeded in providing a good start. I opted out of adding several features for the time being until I need them -- at which point I might have a better understanding of how I could approach it better. I cover some of my thoughts below in this document.
7
+ I started this project to create a pretty simple and clean way to create datagrids. I quickly learned that datagrids aren't especially easy to make amazing, and there's a lot of things that one might want to do with them... grouping (with a row that creates a separator), sorting, ordering, additional informational rows, footers, ajax pagination, etc. etc. etc.. The list is actually pretty long, but I've only written a few of those things. This is a good start, and if it doesn't contain the features you're looking for feel free to fork and add them.
10
8
 
11
9
  So, as always, I set up some requirements for what the project should do, and here's what I came up with (a lot of these are standard):
12
10
 
@@ -22,13 +20,13 @@ So, as always, I set up some requirements for what the project should do, and he
22
20
  I wrote a DSL that met those requirements:
23
21
 
24
22
  <pre>
25
- <% semantic_list_for @users do |l| %>
23
+ <% semantic_list_for @users, :as => :user do |l| %>
26
24
  <%= l.head do %>
27
- <%= l.column 'Name', :sort => 'last_name' %>
28
- <%= l.column 'Email Address' %>
25
+ <%= l.column 'User Login' %>
26
+ <%= l.column 'Email' %>
29
27
  <% end %>
30
28
  <%= l.columns do |user, index| %>
31
- <%= l.column "#{user.first_name} #{user.last_name}" %>
29
+ <%= l.column :login %>
32
30
  <%= l.column link_to(user.email, "mailto:#{user.email}") %>
33
31
  <%= l.controls link_to('edit', edit_user_path(user)) %>
34
32
  <% end %>
@@ -38,22 +36,22 @@ I wrote a DSL that met those requirements:
38
36
  <% end %>
39
37
  </pre>
40
38
 
41
- I liked this, but could it be reduced further if the use case allowed? I came up with the following, which simplifies it, but comes with a minor snag. Note the line that instantiates a new User object. This is needed if there's no objects in the collection you pass it, because it needs to know about the object while it's building the columns.
39
+ I liked this, but could it be reduced further if the use case allowed? I came up with the following (which outlines the different ways you can pass args)
42
40
 
43
41
  <pre>
44
- <% semantic_list_for @users do |l| %>
42
+ <% semantic_list_for @users, :as => :user do |l| %>
45
43
  <%= l.columns [:head, :body] do |user, index| %>
46
- <% user ||= User.new # this DSL requires a user object to be here %>
47
- <%= l.column user.login, :title => 'Login', :sort => 'login' %>
48
- <%= l.column user.email, :title => 'Email Address', :sort => 'email' %>
44
+ <%= l.column :login, :title => 'User Login' %>
45
+ <%= l.column "#{user.first_name} #{user.last_name}", :title => 'Name' %>
46
+ <%= l.column :email, { link_to user.email, "mailto:#{user.email}" } %>
49
47
  <%= l.controls link_to('edit', edit_user_path(user)) %>
50
48
  <% end %>
51
49
  <% end %>
52
50
  </pre>
53
51
 
54
- Clearly there's some ups and downs here, so I implemented both DSLs and leave it to your discretion to figure out which one is best to use in your case.
52
+ I implemented both DSLs and leave it to your discretion on which one is best to use in your case.
55
53
 
56
- Since using builders on previous projects has worked out fairly well, I ventured down that path again. There's a single builder provided for now, TableBuilder (which inherits from GenericBuilder). GenericBuilder provides some basic functionality (ordering links for example), and isn't intended to be used as a builder by itself. And of course, the builders can always be extended or replaced if you need more custom markup.
54
+ Since using builders on previous projects has worked out fairly well, I tried the same thing here. There's a single builder provided for now, TableBuilder (which inherits from GenericBuilder). GenericBuilder provides some basic functionality (ordering links for example), and isn't intended to be used as a builder by itself. And as always, the builders can be extended or replaced if you need more custom markup.
57
55
 
58
56
  h2. Installation
59
57
 
@@ -69,33 +67,26 @@ Then install the listalicious gem:
69
67
  sudo gem install listalicious
70
68
  </pre>
71
69
 
72
- If you would like to get the default JS and CSS files you can use the generator after installing:
73
-
74
- <pre>
75
- [no javascript or css provided yet]
76
- </pre>
77
-
78
70
  h2. Usage
79
71
 
80
- Listalicious works fine just as a view helper, but it also handles ordering of the lists. If you plan on using ordering in the lists you'll have to add a bit to the model and controller. All of the methods are added to the model by using sortable_fields, and the :order option on finders can be generated by using sort_order_from in the controller.
72
+ Listalicious works fine as a simple view helper, but it also handles ordering of the lists as well. To add ordering to any list just use the order_from named scope with your find method. You can also specify fields that are allowed to be ordered in your model by using the orderable_fields method.
81
73
 
82
- h3. Models
74
+ h3. Controllers
83
75
 
84
76
  <pre>
85
- class User < ActiveRecord::Base
86
- # communicates to Listalicious how a list of this model can be ordered
87
- sortable_fields :first_name, :last_name, :login, :email, :default => :login
77
+ def index
78
+ @user = User.ordered_from(params).paginate :page => params[:page], :per_page => 20
79
+ # respond ...
88
80
  end
89
81
  </pre>
90
82
 
91
- h3. Controllers
83
+ h3. Models
92
84
 
93
85
  <pre>
94
- def index
95
- @users = User.paginate :page => params[:page], :per_page => 2, :order => User.sort_order_from(params)
96
- respond_to do |format|
97
- format.html # index.html.erb
98
- format.xml { render :xml => @users }
86
+ class User < ActiveRecord::Base
87
+ orderable_fields do
88
+ only :first_name, :last_name, :login, :email
89
+ default :login, :desc, :stable => true
99
90
  end
100
91
  end
101
92
  </pre>
@@ -104,16 +95,13 @@ h3. Views (I prefer HAML, so no ERB examples, but it should work with ERB fine a
104
95
 
105
96
  It's important to note that all the methods take blocks or content strings. For instance, look at the extra and controls methods in the simple usage example.
106
97
 
107
- It's also important to notice that the controls cell is not being counted in the columns. This is because I position these absolutely and do a hover event on them.. I'll likely take this out shortly so it doesn't effect anyone else.
108
-
109
98
  *Simple Usage*
110
99
 
111
100
  <pre>
112
- - semantic_list_for @users, :html => {:class => 'list'} do |l|
101
+ - semantic_list_for @users, :as => :user, :html => {:class => 'list'} do |l|
113
102
  = l.columns [:head, :body] do |user, index|
114
- - user ||= User.new # this method requires a user object
115
- = l.column user.login, :title => 'Login', :sort => 'login', :width => '20%'
116
- = l.column user.email, :title => 'Email Address', :sort => 'email'
103
+ = l.column :login, :width => '20%'
104
+ = l.column :email, :title => 'Email Address'
117
105
  = l.extra do
118
106
  = "You can add more information about #{user.first_name} here."
119
107
  = l.controls link_to('edit', edit_user_path(user))
@@ -134,7 +122,7 @@ It's also important to notice that the controls cell is not being counted in the
134
122
  <td class="controls"><a href="/users/1/edit">edit</a></td>
135
123
  </tr>
136
124
  <tr class="even">
137
- <td colspan="2">You can add more information about Jeremy here.</td>
125
+ <td colspan="3">You can add more information about Jeremy here.</td>
138
126
  </tr>
139
127
  <tr class="odd">
140
128
  <td>user1</td>
@@ -142,7 +130,7 @@ It's also important to notice that the controls cell is not being counted in the
142
130
  <td class="controls"><a href="/users/2/edit">edit</a></td>
143
131
  </tr>
144
132
  <tr class="odd">
145
- <td colspan="2">You can add more information about User here.</td>
133
+ <td colspan="3">You can add more information about User here.</td>
146
134
  </tr>
147
135
  </tbody>
148
136
  </table>
@@ -168,7 +156,7 @@ It's also important to notice that the controls cell is not being counted in the
168
156
  <table class="semantic-list" id="user_list">
169
157
  <thead>
170
158
  <tr class="header">
171
- <th width="20%"><a class="sort-ascending" href="?user_sort_asc=last_name">Name</a></th>
159
+ <th width="20%"><a class="sort-ascending" href="?user_sort_desc=last_name">Name</a></th>
172
160
  <th>Email Address</th>
173
161
  </tr>
174
162
  </thead>
@@ -186,7 +174,7 @@ It's also important to notice that the controls cell is not being counted in the
186
174
  </tbody>
187
175
  <tfoot>
188
176
  <tr>
189
- <th colspan="2">
177
+ <th colspan="3">
190
178
  <div class="pagination">[removed for your sanity]</div>
191
179
  </th>
192
180
  </tr>
@@ -197,39 +185,39 @@ It's also important to notice that the controls cell is not being counted in the
197
185
  *Grouping* -- You can group lists, and this will add extra header rows as separators. This will include sort links if those were provided as well.
198
186
 
199
187
  <pre>
200
- - semantic_list_for @users, :group_by => :login do |l|
188
+ - semantic_list_for @users, :as => :user, :group_by => :login do |l|
201
189
  </pre>
202
190
 
203
191
  *Sorting* -- Sorting requires javascript. It's part of the javascript code that comes with Listalicious and requires Prototype.js. If you want to add sorting to the list, just provide a url for a sort action and it will put that into the HTML5 data-sorturl attribute. You can use your own javascript if you would like.
204
192
 
205
193
  <pre>
206
- - semantic_list_for @users, :sort_url => { :action => 'sort' } do |l|
194
+ - semantic_list_for @users, :as => :user, :sort_url => { :action => 'sort' } do |l|
207
195
  </pre>
208
196
 
209
- *Additional JS Functionality* -- There a few functions that are javascript specific. You can add any additional functionality this way as well, since we're using unobtrusive JS to get the following features accomplished.
197
+ *Additional JS Functionality* -- There a few functions that are javascript specific. You can add any additional functionality this way as well since we're using unobtrusive JS to get the following features accomplished.
210
198
 
211
199
  You can make the list multi-selectable. There isn't any pre-bundled functionality to do actions on the selected items, but you can fetch the list of selected items by using JS.
212
200
 
201
+ TODO: Document usage here
202
+
213
203
  <pre>
214
- - semantic_list_for @users, :selectable => true do |l|
204
+ - semantic_list_for @users, :as => :user, :selectable => true do |l|
215
205
  </pre>
216
206
 
217
207
  You can make the "extra" information rows expandable. If you add this, the extra rows won't show unless the row they belong to is highlighted.
218
208
 
219
209
  <pre>
220
- - semantic_list_for @users, :expandable => true do |l|
210
+ - semantic_list_for @users, :as => :user, :expandable => true do |l|
221
211
  </pre>
222
212
 
223
213
  h3. UL / OL Datagrids
224
214
 
225
- The nature of doing a UL / OL based datagrid requires a certain level of CSS, and I haven't had time or reason to provide that level yet. When I finish up this project and include the JS and CSS needed for it (in a generator) I may add a builder for this, but there isn't one currently.
215
+ The nature of doing a UL / OL based datagrid requires a certain level of CSS, and I haven't had time or reason to provide that level yet. When I finish up this project and include the JS and CSS needed for it (in a generator) I may add a builder for this, but there isn't plans for one currently.
226
216
 
227
217
  h3. Other
228
218
 
229
219
  It's important to note that a lot of the functionality of these lists do not play nicely with one another -- I don't believe this is a shortcoming, and consider it more an effort to avoid overkill. A good example of this is the sortable features. For example, if you need the extra information and sorting together, you should consider using the :expandable => true option. Grouping and sorting don't play together nicely for obvious reasons as well. The javascript handles moving the extra container, in sorting, but doesn't attempt to do it gracefully. If I do the ListBuilder (UL/OL) to compliment the TableBuilder, it would likely handle these things somewhat better, but I haven't had a need for it yet.
230
220
 
231
- I'm kicking around plans for how to make it more featureful without the annoying conflicts mentioned above. If you have any thoughts let me know, but I expect my solution will come by breaking up the builders by features.. So, instead of having a single TableBuilder, there would be a SortableTableBuilder, and an InformationalTableBuilder, and a MultiSelectTableBuilder, etc.. not sure how that would play out, but that's what I'm thinking might resolve some of the annoyances mentioned above.
232
-
233
221
  And, as always, you can create your own builder by extending one of the existing ones, or by creating one from scratch.
234
222
 
235
223
  Then just specify your builder, or do it as a configuration.
@@ -246,11 +234,7 @@ h2. Documentation
246
234
 
247
235
  RDoc documentation _should_ be automatically generated after each commit and made available on the "rdoc.info website":http://rdoc.info/projects/jejacks0n/listalicious.
248
236
 
249
- Documentation is pretty sparse right now.
250
-
251
- h2. Compatibility
252
-
253
- I'm only testing with the latest Rails 2.4.x stable release, and it should work under Rails 2.3.x as well. Feel free to add backwards compatibility if you need it.
237
+ Documentation is pretty sparse right now, and I'm working to resolve it.
254
238
 
255
239
  h2. Project Info
256
240
 
data/Rakefile CHANGED
File without changes
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.3.0
@@ -5,28 +5,33 @@ module Listalicious
5
5
 
6
6
  def initialize(template, collection, options = {}, &proc)
7
7
  @template, @collection, @options = template, collection, options
8
- @object_name = collection.first.class.name.underscore unless collection.empty?
8
+ @object_name = "#{options[:as] || collection.first ? collection.first.class.name.underscore : ''}"
9
+ @table_name = (options[:table_name] || @object_name.pluralize).to_s
9
10
 
10
11
  render(options.delete(:html), &proc)
11
12
  end
12
13
 
13
14
  def render(options, &proc); end
14
15
 
15
- def sortable_link(contents, field)
16
- sort_url, sort_direction = sortable_params(field)
17
- template.content_tag(:a, contents, :href => "?#{sort_url}", :class => "sort-#{sort_direction == 'descending' ? 'ascending' : 'descending'}")
18
- end
16
+ # Creates the anchor tag for the orderable links.
17
+ # It looks in the params for specificly styled query params:
18
+ #
19
+ # eg. order[users][]=login:asc&order[users][]=first_name:asc
20
+ def orderable_link(contents, field)
21
+ return contents if @object_name.empty?
19
22
 
20
- def sortable_params(field)
21
- object_name = @object_name.nil? ? '' : "#{@object_name}_"
23
+ order_params = (template.params['order'] || {})[@table_name]
24
+ fields = Hash[*order_params.to_a.collect { |field_and_dir| field_and_dir.split(':') }.flatten]
22
25
 
23
- params = template.params.reject { |param, value| ['action', 'controller', "#{object_name}sort_desc"].include?(param) }
24
- direction = params.delete("#{object_name}sort_asc") == field.to_s ? 'descending' : 'ascending'
25
- method = direction == 'descending' ? "#{object_name}sort_desc" : "#{object_name}sort_asc"
26
- params[method] = field
26
+ order_params = (template.params['order'] || {}).clone.
27
+ merge({@table_name => ["#{field}:#{"#{fields[field] == 'asc' ? 'desc' : 'asc'}"}"]})
27
28
 
28
- [params.to_query, direction]
29
- end
29
+ query = template.params.reject{ |param, value| ['action', 'controller'].include?(param) }
30
+ query.merge!('order' => order_params)
30
31
 
32
+ template.content_tag(:a, contents,
33
+ :href => "?#{query.to_query}",
34
+ :class => "#{fields[field] ? fields[field] == 'asc' ? 'ascending' : 'descending' : ''}")
35
+ end
31
36
  end
32
37
  end
@@ -40,7 +40,7 @@ module Listalicious
40
40
 
41
41
  def column_group_head(options = {}, &proc)
42
42
  @column_count = 0
43
-
43
+
44
44
  @head_wrapper = template.content_tag(:tr, template.capture(collection.first, 0, &proc),
45
45
  options[:html].merge({:class => template.add_class(options[:html][:class], 'header')}))
46
46
  template.content_tag(:thead, @head_wrapper, options.delete(:wrapper_html))
@@ -51,7 +51,8 @@ module Listalicious
51
51
  return unless collection.first.present?
52
52
  collection.each_with_index do |record, index|
53
53
  @column_count = 0
54
-
54
+ @object = record
55
+
55
56
  if @options[:grouped_by]
56
57
  buffer << @head_wrapper if record[@options[:grouped_by]] != @last_row_grouping
57
58
  @last_row_grouping = record[@options[:grouped_by]]
@@ -61,7 +62,7 @@ module Listalicious
61
62
  buffer << template.content_tag(:tr, template.capture(record, index, &proc),
62
63
  options[:html].merge({:class => template.add_class(options[:html][:class], @cycle)}))
63
64
  buffer << template.content_tag(:tr, @extra,
64
- options[:html].merge({:class => template.add_class(options[:html][:class], @cycle)})) if @extra.present?
65
+ options[:html].merge({:class => template.add_class(options[:html][:class], [@cycle, 'extra'])})) if @extra.present?
65
66
  @extra = nil
66
67
  end
67
68
  template.content_tag(:tbody, buffer, options.delete(:wrapper_html))
@@ -93,9 +94,6 @@ module Listalicious
93
94
  options[:html] ||= {}
94
95
  options[:html][:colspan] ||= @column_count
95
96
 
96
- options[:wrapper_html] ||= {}
97
- options[:wrapper_html][:class] = template.add_class(options[:wrapper_html][:class], 'full-column')
98
-
99
97
  @extra = self.send("#{@current_scope}_column", contents, options)
100
98
  end
101
99
 
@@ -118,9 +116,6 @@ module Listalicious
118
116
  options[:html] ||= {}
119
117
  options[:html][:colspan] ||= @column_count
120
118
 
121
- options[:wrapper_html] ||= {}
122
- options[:wrapper_html][:class] = template.add_class(options[:wrapper_html][:class], 'extra')
123
-
124
119
  @extra = self.send("#{@current_scope}_column", contents, options)
125
120
  ''
126
121
  end
@@ -128,21 +123,24 @@ module Listalicious
128
123
  def head_column(contents, options = {})
129
124
  options[:html] ||= {}
130
125
  options[:html][:width] ||= options[:width]
131
-
132
- contents = sortable_link(contents, options[:sort]) if options[:sort]
126
+
127
+ contents = contents.to_s.humanize.titleize if contents.is_a? Symbol
128
+ contents = orderable_link(contents, options[:sort]) if options[:sort]
133
129
 
134
130
  template.content_tag(:th, contents, options.delete(:html))
135
131
  end
136
132
 
137
133
  def body_column(contents, options = {})
134
+ contents = @object.send(contents) if @object && contents.is_a?(Symbol)
135
+
138
136
  template.content_tag(:td, contents, options.delete(:html))
139
137
  end
140
138
 
141
139
  def foot_column(contents, options = {})
142
- contents = sortable_link(contents, options[:sort]) if options[:sort]
140
+ contents = orderable_link(contents, options[:sort]) if options[:sort]
143
141
 
144
142
  template.content_tag(:th, contents, options.delete(:html))
145
143
  end
146
-
144
+
147
145
  end
148
146
  end
@@ -1,6 +1,6 @@
1
1
  # coding: utf-8
2
- require File.join(File.dirname(__FILE__), *%w[builders generic_builder])
3
- require File.join(File.dirname(__FILE__), *%w[builders table_builder])
2
+ require 'builders/generic_builder'
3
+ require 'builders/table_builder'
4
4
 
5
5
  module Listalicious #:nodoc:
6
6
 
@@ -13,23 +13,23 @@ module Listalicious #:nodoc:
13
13
  @@builder = ::Listalicious::TableBuilder
14
14
  mattr_accessor :builder
15
15
 
16
- def semantic_list_for(collection, *args, &proc)
16
+ def semantic_list_for(collection, options, &proc)
17
17
  raise ArgumentError, "Missing block" unless block_given?
18
18
 
19
- options = args.extract_options!
19
+ # TODO: should :as be required?
20
20
 
21
21
  options[:html] ||= {}
22
22
  options[:html][:class] = add_class(options[:html][:class], 'semantic-list')
23
- options[:html][:id] ||= collection.first ? "#{collection.first.class.name.underscore}_list" : 'semantic_list'
24
-
25
- options[:html][:class] = add_class(options[:html][:class], 'actionable') if options[:actionable]
26
- options[:html][:class] = add_class(options[:html][:class], 'selectable') if options[:selectable]
23
+ options[:html][:id] ||= "#{options[:as] || collection.first ? collection.first.class.name.underscore : 'semantic'}_list"
27
24
 
28
25
  if options[:sort_url]
29
26
  options[:html][:class] = add_class(options[:html][:class], 'sortable')
30
27
  options[:html]['data-sorturl'] = url_for(options[:sort_url])
31
28
  end
32
29
 
30
+ options[:html][:class] = add_class(options[:html][:class], 'selectable') if options[:selectable]
31
+ options[:html][:class] = add_class(options[:html][:class], 'expandable') if options[:expandable]
32
+
33
33
  builder = options[:builder] || TableBuilder
34
34
  builder.new(@template, collection, options, &proc)
35
35
  end
@@ -43,35 +43,105 @@ module Listalicious #:nodoc:
43
43
 
44
44
  module ActiveRecordExtensions # :nodoc:
45
45
 
46
- def self.included(base) # :nodoc:
47
- return if base.kind_of?(::Listalicious::ActiveRecordExtensions::ClassMethods)
48
- base.class_eval do
49
- extend ClassMethods
50
- end
46
+ # Makes a given model orderable for lists
47
+ #
48
+ # To specify that a model behaves according to the Listalicious order style call orderable_fields. The
49
+ # orderable_fields method takes a configuration block.
50
+ #
51
+ # === Example
52
+ # orderable_fields do
53
+ # only :first_name, :last_name
54
+ # default :last_name
55
+ # end
56
+ #
57
+ # === Configuration Methods
58
+ # [only]
59
+ # Provide fields that are orderable.
60
+ # [except]
61
+ # Provide fields that are not orderable, with the default list being all fields.
62
+ # [default]
63
+ # Provide the default sort field, optionally a direction, and additional options.
64
+ #
65
+ # *Notes*:
66
+ # * If +only+ or +except+ are not called within the block, all fields on the model will be orderable, this includes
67
+ # things like id, and password/password salt columns.
68
+ # * If +default+ isn't called, the first field will be considered the default, asc being the default direction.
69
+ #
70
+ def orderable_fields(&config_block)
71
+ cattr_accessor :orderable_fields, :default_order
72
+
73
+ # make all columns orderable, incase only or except aren't called in the configuration block
74
+ self.orderable_fields = column_names.map { |column_name| column_name.to_s }
75
+
76
+ instance_eval(&config_block)
77
+ self.orderable_fields.collect!{ |field| field.to_s }
78
+
79
+ self.default_order ||= {:field => self.orderable_fields.first, :direction => :desc}
80
+
81
+ attach_orderable_scopes
51
82
  end
52
83
 
53
- module ClassMethods
54
-
55
- attr_accessor :default_sort_field
56
-
57
- def sortable_fields(*args)
58
- options = args.extract_options!
59
- @acceptable_sort_fields = args
60
- @default_sort_field = options[:default]
61
- end
84
+ # Provide fields that are orderable in the configuration block.
85
+ #
86
+ # *Note*: If +only+ or +except+ aren't called from within the configuration block, all fields will be orderable.
87
+ #
88
+ # === Example
89
+ # only :last_name, :email_address
90
+ def only(*args)
91
+ self.orderable_fields = args
92
+ end
62
93
 
63
- def acceptable_sort_field?(column)
64
- column.present? ? @acceptable_sort_fields.include?(column.to_sym) : false
65
- end
94
+ # Provide fields that are not to be orderable, with the default list being all fields.
95
+ #
96
+ # *Note*: If +only+ or +except+ aren't called from within the configuration block, all fields will be orderable.
97
+ #
98
+ # === Example
99
+ # * except :id, :password
100
+ def except(*args)
101
+ self.orderable_fields - args
102
+ end
66
103
 
67
- def sort_order_from(params)
68
- field = params["#{self.name.underscore}_sort_asc"] || params["#{self.name.underscore}_sort_desc"]
69
- field = @default_sort_field.to_s unless acceptable_sort_field?(field)
104
+ # Provide the default sort field, optionally a direction, and additional options.
105
+ #
106
+ # === Supported options
107
+ # [:stable]
108
+ # Will force appending the default sort to the end of all sort requests. Default is false.
109
+ #
110
+ # === Example
111
+ # default :first_name (direction defaults to :asc)
112
+ # default :first_name, :stable => true
113
+ # default :first_name, :desc, :stable => true
114
+ def default(*args)
115
+ options = args.extract_options!
116
+ field = args.shift
117
+ direction = args.shift || :asc
70
118
 
71
- method = (params["#{self.name.underscore}_sort_desc".to_sym] == field) ? 'DESC' : 'ASC'
72
- "#{field} #{method}" unless field.blank?
73
- end
119
+ self.default_order = {:field => field, :direction => direction, :options => options}
120
+ end
74
121
 
122
+ # Attaches the ordered_from named scope to the model requesting it. The named scope can be chained in the
123
+ # controller by using:
124
+ #
125
+ # +Users.ordered_by(params).paginate :page => params[:page], :per_page => 2+
126
+ #
127
+ # The params are expected to be in a specific style:
128
+ #
129
+ # eg. order[table_name][]=last_name:desc&order[table_name][]=first_name:asc
130
+ #
131
+ # Which will generate the order clause +"last_name DESC, first_name ASC"+.
132
+ def attach_orderable_scopes
133
+ self.named_scope :ordered_from, lambda { |params|
134
+ return unless params.include?(:order) and params[:order][self.table_name.to_sym]
135
+
136
+ fields = params[:order][self.table_name.to_sym].collect do |field_and_dir|
137
+ field, dir = field_and_dir.split(':')
138
+ self.orderable_fields.include?(field) ? [field, dir.to_s.downcase] : nil
139
+ end.compact
140
+
141
+ fields.empty? ?
142
+ nil :
143
+ {:order => fields.map{ |field, dir| "#{field} #{dir.downcase == 'desc' ? 'DESC' : 'ASC'}" }.join(', ')}
144
+ }
75
145
  end
76
146
 
77
147
  end
@@ -79,4 +149,4 @@ module Listalicious #:nodoc:
79
149
  end
80
150
 
81
151
  ActionController::Base.helper Listalicious::SemanticListHelper
82
- ActiveRecord::Base.class_eval { include ::Listalicious::ActiveRecordExtensions }
152
+ ActiveRecord::Base.extend Listalicious::ActiveRecordExtensions
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{listalicious}
8
- s.version = "0.1.0"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jeremy Jackson"]
12
- s.date = %q{2010-01-24}
12
+ s.date = %q{2010-03-02}
13
13
  s.description = %q{Semantic listing; a semantic way to build datagrid structures in Rails.}
14
14
  s.email = %q{jejacks0n@gmail.com}
15
15
  s.extra_rdoc_files = [
File without changes
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: listalicious
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Jackson
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-01-24 00:00:00 -07:00
12
+ date: 2010-03-02 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency