sorting_table_for 0.1.0
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/MIT-LICENSE +20 -0
- data/README.mdown +213 -0
- data/Rakefile +11 -0
- data/init.rb +7 -0
- data/lib/model/sorting_table_model_scope.rb +53 -0
- data/lib/sorting_table_for.rb +37 -0
- data/lib/sorting_table_for/i18n.rb +36 -0
- data/lib/sorting_table_for/table_builder.rb +366 -0
- data/lib/sorting_table_for/tools.rb +43 -0
- data/spec/db/database.yml +3 -0
- data/spec/db/schema.rb +20 -0
- data/spec/fixtures/user.rb +24 -0
- data/spec/helpers/builder_spec.rb +47 -0
- data/spec/helpers/cell_value_spec.rb +85 -0
- data/spec/helpers/column_spec.rb +359 -0
- data/spec/helpers/header_spec.rb +337 -0
- data/spec/helpers/i18n_spec.rb +54 -0
- data/spec/locales/test.yml +92 -0
- data/spec/locales/test_rails3.yml +92 -0
- data/spec/model/sorting_table_model_scope_spec.rb +89 -0
- data/spec/spec_helper.rb +70 -0
- metadata +89 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 arkownz (tom)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.mdown
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
# SortingTableFor
|
2
|
+
|
3
|
+
## Introduction
|
4
|
+
|
5
|
+
SortingTableFor is a Rails TableBuilder made to easily create table or sort a table. The syntax is simple to write and easy to read.
|
6
|
+
|
7
|
+
## Infos
|
8
|
+
|
9
|
+
- It's Rails 2.3 and 3 compatible
|
10
|
+
- I18n compatible
|
11
|
+
- Almost fully configurable
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Install with git
|
16
|
+
|
17
|
+
cd your_rails_app
|
18
|
+
cd vendor/plugins
|
19
|
+
git clone git://github.com/arkownz/sorting_table_for.git
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
To create a simple table
|
24
|
+
|
25
|
+
<%- sorting_table_for @users do |table| %>
|
26
|
+
<%= table.headers %>
|
27
|
+
<%= table.columns %>
|
28
|
+
<%- end %>
|
29
|
+
|
30
|
+
will render
|
31
|
+
|
32
|
+
<table>
|
33
|
+
<thead>
|
34
|
+
<tr>
|
35
|
+
<th><a href='/to_sort'>Username</a></th>
|
36
|
+
<th><a href='/to_sort'>Firstname</a></th>
|
37
|
+
<th>Edit</th>
|
38
|
+
<th>Delete</th>
|
39
|
+
</tr>
|
40
|
+
</thead>
|
41
|
+
<tbody>
|
42
|
+
<tr>
|
43
|
+
<td colspan='4'>Total rows : 1</td>
|
44
|
+
</tr>
|
45
|
+
<tr>
|
46
|
+
<td>Test</td>
|
47
|
+
<td>Myname</td>
|
48
|
+
<td><a href='/to_edit'>Edit</a></td>
|
49
|
+
<td><a href='/to_delete'>Delete</a></td>
|
50
|
+
<tr>
|
51
|
+
</tbody>
|
52
|
+
</table>
|
53
|
+
|
54
|
+
You can specify columns and headers
|
55
|
+
|
56
|
+
<%- sorting_table_for @users do |table| %>
|
57
|
+
<%= table.headers :username, :firstname, :lastname %>
|
58
|
+
<%= table.columns :username, :firstname, :lastname %>
|
59
|
+
<%- end %>
|
60
|
+
|
61
|
+
On columns you can get the object of your collection.
|
62
|
+
Your can pass to column or header what ever your want: symbol, string, image, ...
|
63
|
+
|
64
|
+
<%- sorting_table_for @users do |table| %>
|
65
|
+
<%= table.headers do %>
|
66
|
+
<%= table.header :username %>
|
67
|
+
<%= table.header :firstname %>
|
68
|
+
<%= table.header image_tag('rails.png') %>
|
69
|
+
<%- end %>
|
70
|
+
<%= table.columns do |user| %>
|
71
|
+
<%= table.column :username %>
|
72
|
+
<%= table.column user.firstname %>
|
73
|
+
<%= table.column 'exemple' %>
|
74
|
+
<%- end %>
|
75
|
+
<%- end %>
|
76
|
+
|
77
|
+
For more control on headers or columns
|
78
|
+
|
79
|
+
<%- sorting_table_for @users do |table| %>
|
80
|
+
<%= table.headers do %>
|
81
|
+
<%= table.header do %>
|
82
|
+
<%= image_tag('rails.png') %>
|
83
|
+
<%- end %>
|
84
|
+
<%- end %>
|
85
|
+
<%= table.columns do |val| %>
|
86
|
+
<%= table.column do %>
|
87
|
+
<%= val.username.downcase %>
|
88
|
+
<%- end %>
|
89
|
+
<%- end %>
|
90
|
+
<%- end %>
|
91
|
+
|
92
|
+
By default SortingTableFor will customize your table
|
93
|
+
|
94
|
+
Header:
|
95
|
+
|
96
|
+
- On symbol it make by default a sorting
|
97
|
+
- On symbol it will add the translation (I18n.t)
|
98
|
+
- On sorting it will add a class with an image to show the sort order
|
99
|
+
|
100
|
+
Column:
|
101
|
+
|
102
|
+
- On Symbol it will find the value in collection
|
103
|
+
- On Object Date or DateTime it will add the localization (I18n.l)
|
104
|
+
- On Column name (price, total_price, ...) it will the currency
|
105
|
+
- On Boolean it will add the translation (I18n.t) true or false
|
106
|
+
|
107
|
+
You can customize the table
|
108
|
+
|
109
|
+
<%- sorting_table_for @users do |table| %>
|
110
|
+
<%= table.headers do %>
|
111
|
+
<%= table.header :username %>
|
112
|
+
<%= table.header :price, :sort => false %>
|
113
|
+
<%= table.header :created_at %>
|
114
|
+
<%= table.header 'today' %>
|
115
|
+
<%- end %>
|
116
|
+
<%= table.columns do |user| %>
|
117
|
+
<%= table.column user.username %>
|
118
|
+
<%= table.column user.price, :as => :currency %>
|
119
|
+
<%= table.column user.created, :as => :date %>
|
120
|
+
<%= table.column DateTime.now, :as => :datetime, :format => :short %>
|
121
|
+
<%- end %>
|
122
|
+
<%- end %>
|
123
|
+
|
124
|
+
You can customize the table with action
|
125
|
+
|
126
|
+
<%- sorting_table_for @users do |table| %>
|
127
|
+
<%= table.headers do %>
|
128
|
+
<%= table.header :username %>
|
129
|
+
<%= table.header :action => :edit %>
|
130
|
+
<%- end %>
|
131
|
+
<%= table.columns do |user| %>
|
132
|
+
<%= table.column user.username, :action => :show %>
|
133
|
+
<%= table.column :action => :edit %>
|
134
|
+
<%- end %>
|
135
|
+
<%- end %>
|
136
|
+
|
137
|
+
You can customize the html and add title
|
138
|
+
|
139
|
+
<%- sorting_table_for @users, :html => { :class => 'my_table' } do |table| %>
|
140
|
+
<%= table.headers :html => { :class => 'my_headers', :title => 'column !' } do %>
|
141
|
+
<%= table.header :username :html => { :class => 'header_username' } %>
|
142
|
+
<%= table.header :firstname :html => { :title => 'hello price' } %>
|
143
|
+
<%- end %>
|
144
|
+
<%= table.columns :html => { :class => 'my_columns' } do |user| %>
|
145
|
+
<%= table.column :username :html => { :class => 'column_username' }%>
|
146
|
+
<%= table.column :firstname :html => { :title => "it's #{val.firstname}" } %>
|
147
|
+
<%- end %>
|
148
|
+
<%- end %>
|
149
|
+
|
150
|
+
## Namespace
|
151
|
+
|
152
|
+
SortingTableFor can use your namespace
|
153
|
+
|
154
|
+
<%- sorting_table_for [:admin, @users] do |table| %>
|
155
|
+
<%= table.headers %>
|
156
|
+
<%= table.columns %>
|
157
|
+
<%- end %>
|
158
|
+
|
159
|
+
## Sorting
|
160
|
+
|
161
|
+
To add sorting in your query, you just have to add sorting_table in your query
|
162
|
+
|
163
|
+
def index
|
164
|
+
@users = User.sorting_table(params).all
|
165
|
+
end
|
166
|
+
|
167
|
+
to add a default sorting
|
168
|
+
|
169
|
+
def index
|
170
|
+
@users = User.sorting_table(params, :username).all
|
171
|
+
end
|
172
|
+
|
173
|
+
-- or --
|
174
|
+
|
175
|
+
def index
|
176
|
+
@users = User.sorting_table(params, :username, :desc).all
|
177
|
+
end
|
178
|
+
|
179
|
+
## Ajax
|
180
|
+
|
181
|
+
You can add ajax on sorting
|
182
|
+
|
183
|
+
<%- sorting_table_for @users, :sort_remote => true do |table| %>
|
184
|
+
<%= table.headers %>
|
185
|
+
<%= table.columns %>
|
186
|
+
<%- end %>
|
187
|
+
|
188
|
+
You can add ajax on links
|
189
|
+
|
190
|
+
<%- sorting_table_for @users, :link_remote => true do |table| %>
|
191
|
+
<%= table.headers %>
|
192
|
+
<%= table.columns %>
|
193
|
+
<%- end %>
|
194
|
+
|
195
|
+
## Stylesheet
|
196
|
+
|
197
|
+
there are a default stylesheet in _stylesheet_
|
198
|
+
|
199
|
+
- A class 'odd' or 'even' on rows
|
200
|
+
- A class 'cur-sort-not', 'cur-sort-asc' or 'cur-sort-desc' on headers that you want to sort
|
201
|
+
|
202
|
+
[_Here an exemple of css file_][]
|
203
|
+
|
204
|
+
## Configurations
|
205
|
+
|
206
|
+
There are some options that you can customize in your initiatilizer
|
207
|
+
|
208
|
+
[_see the initializer file exemple for more explanation_][]
|
209
|
+
|
210
|
+
Copyright (c) 2010 arkownz (tom), released under the MIT license
|
211
|
+
|
212
|
+
[_see the initializer file exemple for more explanation_]: http://github.com/arkownz/sorting_table_for/blob/master/assets/config/initializers/sorting_table_for.rb
|
213
|
+
[_Here an exemple of css file_]: http://github.com/arkownz/sorting_table_for/blob/master/assets/public/stylesheets/sorting_table_for.css
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
desc 'Default: run specs.'
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc 'Run the specs'
|
8
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
9
|
+
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
10
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
11
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SortingTableModelScope
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(SingletonMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module SingletonMethods
|
10
|
+
|
11
|
+
def sorting_table(*args)
|
12
|
+
raise ArgumentError, 'sorting_table: Too many arguments (max : 3)' if args.size > 3
|
13
|
+
sort_table_param = get_sorting_table_params(args)
|
14
|
+
return scoped({}) if !sort_table_param and args.size == 1
|
15
|
+
sort, direction = get_sort_and_direction(sort_table_param, args)
|
16
|
+
return scoped({}) if !sort or !valid_column?(sort) or !valid_direction?(direction)
|
17
|
+
return scoped({ :order => "#{sort} #{direction}" })
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def get_sorting_table_params(args)
|
23
|
+
return nil unless args.first.is_a? Hash
|
24
|
+
return nil unless args.first.has_key? SortingTableFor::TableBuilder.params_sort_table.to_s
|
25
|
+
args.first[SortingTableFor::TableBuilder.params_sort_table.to_s]
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_sort_and_direction(sort_table_param, args)
|
29
|
+
if sort_table_param
|
30
|
+
key = sort_table_param.keys.first rescue nil
|
31
|
+
value = sort_table_param.values.first rescue nil
|
32
|
+
return nil if !key.is_a?(String) or !value.is_a?(String)
|
33
|
+
return key, value
|
34
|
+
end
|
35
|
+
return nil if args.size < 2
|
36
|
+
return args[1], 'asc' if args.size == 2
|
37
|
+
return args[1], args[2]
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid_column?(column)
|
41
|
+
column_names.include? column.to_s.downcase
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid_direction?(direction)
|
45
|
+
%[asc desc].include? direction.to_s.downcase
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if defined? ActiveRecord
|
52
|
+
ActiveRecord::Base.send :include, SortingTableModelScope
|
53
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'sorting_table_for/table_builder'
|
4
|
+
require 'sorting_table_for/i18n'
|
5
|
+
require 'sorting_table_for/tools'
|
6
|
+
|
7
|
+
module SortingTableFor
|
8
|
+
|
9
|
+
def sorting_table_for(object_or_array, *args)
|
10
|
+
raise ArgumentError, 'Missing block' unless block_given?
|
11
|
+
options = args.extract_options!
|
12
|
+
html_options = (options[:html]) ? options[:html].merge(:class => "#{options[:html][:class]} sorting_table_for".strip) : { :class => :sorting_table_for }
|
13
|
+
builder = options[:builder] || TableBuilder
|
14
|
+
case object_or_array
|
15
|
+
when Array
|
16
|
+
if object_or_array.last.is_a?(Array)
|
17
|
+
object = object_or_array.last
|
18
|
+
object_or_array.pop
|
19
|
+
else
|
20
|
+
object = object_or_array
|
21
|
+
object_or_array = []
|
22
|
+
end
|
23
|
+
else
|
24
|
+
object = object_or_array
|
25
|
+
object_or_array = []
|
26
|
+
end
|
27
|
+
content_tag(:table, html_options) do
|
28
|
+
yield builder.new(object, object_or_array, self, options, params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
# Include in ActionView Helper
|
35
|
+
if defined? ActionView
|
36
|
+
ActionView::Base.send(:include, SortingTableFor)
|
37
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SortingTableFor
|
4
|
+
module I18n
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def set_options(params, model_name, namespace)
|
8
|
+
@params, @model_name, @namespace = params, model_name, namespace
|
9
|
+
end
|
10
|
+
|
11
|
+
def translate(attribute, options = {}, action_header = false)
|
12
|
+
if !options.has_key? :scope
|
13
|
+
options[:scope] = create_scope
|
14
|
+
options[:scope] << TableBuilder.i18n_add_header_action_scope if action_header
|
15
|
+
end
|
16
|
+
::I18n.t(attribute, options)
|
17
|
+
end
|
18
|
+
alias :t :translate
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_scope
|
23
|
+
return TableBuilder.i18n_default_scope.collect do |scope_value|
|
24
|
+
case scope_value.to_sym
|
25
|
+
when :controller then @params[:controller] ? @params[:controller].downcase : ''
|
26
|
+
when :action then @params[:action] ? @params[:action].downcase : ''
|
27
|
+
when :model then @model_name ? @model_name.downcase.to_s : ''
|
28
|
+
when :namespace then @namespace ? @namespace.downcase.to_s : ''
|
29
|
+
else scope_value.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,366 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module SortingTableFor
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
class TableBuilder
|
8
|
+
|
9
|
+
include ::ActionView::Helpers::TagHelper
|
10
|
+
include ::ActionView::Helpers::NumberHelper
|
11
|
+
|
12
|
+
class_inheritable_accessor :reserved_columns, :currency_columns,
|
13
|
+
:default_boolean, :show_total_entries,
|
14
|
+
:params_sort_table, :i18n_default_format_date,
|
15
|
+
:html_sorting_class, :default_actions,
|
16
|
+
:i18n_default_scope, :i18n_add_header_action_scope
|
17
|
+
|
18
|
+
self.reserved_columns = [:id, :password, :salt]
|
19
|
+
self.currency_columns = [:price, :total_price, :currency, :money]
|
20
|
+
self.default_boolean = [::I18n.t(:bolean_true, :scope => [:sorting_table_for, :columns]), ::I18n.t(:bolean_false, :scope => [:sorting_table_for, :columns])]
|
21
|
+
self.show_total_entries = true
|
22
|
+
self.params_sort_table = :table_sort
|
23
|
+
self.i18n_default_format_date = :default
|
24
|
+
self.html_sorting_class = [:'cur-sort-not', :'cur-sort-asc', :'cur-sort-desc']
|
25
|
+
self.default_actions = [:edit, :delete]
|
26
|
+
self.i18n_default_scope = [:namespace, :controller, :action]
|
27
|
+
self.i18n_add_header_action_scope = :header
|
28
|
+
|
29
|
+
def initialize(collection, object_or_array, template, options, params)
|
30
|
+
@collection, @@object_or_array, @@template, @@options, @@params = collection, object_or_array, template, options, params
|
31
|
+
I18n.set_options(params, model_name(@collection.first), object_or_array.first)
|
32
|
+
set_default_global_options
|
33
|
+
@lines = []
|
34
|
+
end
|
35
|
+
|
36
|
+
## Headers
|
37
|
+
|
38
|
+
def headers(*args, &block)
|
39
|
+
column_options, html_options = get_column_and_html_options( args.extract_options! )
|
40
|
+
if block_given?
|
41
|
+
@header_line = FormatLine.new(args, column_options, html_options, nil, :thead)
|
42
|
+
@@template.capture(&block)
|
43
|
+
else
|
44
|
+
@header_line = FormatLine.new(args, column_options, html_options, @collection.first, :thead)
|
45
|
+
end
|
46
|
+
render_thead
|
47
|
+
end
|
48
|
+
|
49
|
+
def header(*args, &block)
|
50
|
+
if block_given?
|
51
|
+
block = @@template.capture(&block)
|
52
|
+
@header_line.add_cell(@collection.first, args, nil, block)
|
53
|
+
else
|
54
|
+
@header_line.add_cell(@collection.first, args)
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
## Columns
|
60
|
+
|
61
|
+
def columns(*args, &block)
|
62
|
+
column_options, html_options = get_column_and_html_options( args.extract_options! )
|
63
|
+
@collection.each do |object|
|
64
|
+
@current_object = object
|
65
|
+
if block_given?
|
66
|
+
@lines << FormatLine.new(args, column_options, html_options)
|
67
|
+
yield(object)
|
68
|
+
else
|
69
|
+
@lines << FormatLine.new(args, column_options, html_options, object)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
render_tbody
|
73
|
+
end
|
74
|
+
|
75
|
+
def column(*args, &block)
|
76
|
+
if block_given?
|
77
|
+
block = @@template.capture(&block)
|
78
|
+
@lines.last.add_cell(@current_object, args, nil, block)
|
79
|
+
else
|
80
|
+
@lines.last.add_cell(@current_object, args)
|
81
|
+
end
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
# This function is a copy from Formtastic: http://github.com/justinfrench/formtastic
|
88
|
+
def model_name(object)
|
89
|
+
object.present? ? object.class.name : object.to_s.classify
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def render_thead
|
95
|
+
if @header_line
|
96
|
+
return Tools::html_safe(content_tag(:thead, @header_line.render_line))
|
97
|
+
end
|
98
|
+
''
|
99
|
+
end
|
100
|
+
|
101
|
+
def render_tbody
|
102
|
+
if @lines and @lines.size > 0
|
103
|
+
return Tools::html_safe(content_tag(:tbody, render_total_entries + Tools::html_safe(@lines.collect { |line| line.render_line }.join)))
|
104
|
+
end
|
105
|
+
''
|
106
|
+
end
|
107
|
+
|
108
|
+
def set_default_global_options
|
109
|
+
@@options[:sort] = true unless @@options.has_key? :sort
|
110
|
+
end
|
111
|
+
|
112
|
+
def render_total_entries
|
113
|
+
if self.show_total_entries
|
114
|
+
total_entries = @collection.total_entries rescue @collection.size
|
115
|
+
return Tools::html_safe(content_tag(:tr, content_tag(:td, I18n.t(:total_entries, :scope => :sorting_table_for, :value => total_entries), {:colspan => @lines.first.total_cells}), { :class => 'total-entries' }))
|
116
|
+
end
|
117
|
+
''
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_column_and_html_options(options)
|
121
|
+
return options, options.delete(:html) || {}
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
## Format Lines
|
128
|
+
##
|
129
|
+
##
|
130
|
+
##
|
131
|
+
|
132
|
+
class FormatLine < TableBuilder
|
133
|
+
|
134
|
+
def initialize(args, column_options = {}, html_options = {}, object = nil, type = nil)
|
135
|
+
@args, @column_options, @html_options, @object, @type = args, column_options, html_options, object, type
|
136
|
+
@cells = []
|
137
|
+
if object
|
138
|
+
@attributes = (args.empty?) ? (get_columns - TableBuilder.reserved_columns) : @args
|
139
|
+
create_cells
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def add_cell(object, args, type =nil, block = nil)
|
144
|
+
@cells << FormatCell.new(object, args, type, block)
|
145
|
+
end
|
146
|
+
|
147
|
+
def render_line
|
148
|
+
if @type == :thead
|
149
|
+
header = content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_thead }.join), @html_options)
|
150
|
+
else
|
151
|
+
content_tag(:tr, Tools::html_safe(@cells.collect { |cell| cell.render_cell_tbody }.join), @html_options.merge(:class => "#{@html_options[:class]} #{@@template.cycle(:odd, :even)}".strip))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def total_cells
|
156
|
+
@cells.size.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
protected
|
160
|
+
|
161
|
+
# This function is a copy from Formtastic: http://github.com/justinfrench/formtastic
|
162
|
+
def content_columns
|
163
|
+
model_name(@object).constantize.content_columns.collect { |c| c.name.to_sym }.compact rescue []
|
164
|
+
end
|
165
|
+
|
166
|
+
def model_have_column?(column)
|
167
|
+
model_name(@object).constantize.content_columns.each do |model_column|
|
168
|
+
return true if model_column.name == column.to_s
|
169
|
+
end
|
170
|
+
false
|
171
|
+
end
|
172
|
+
|
173
|
+
def can_sort_column?(column)
|
174
|
+
model_have_column?(column)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def create_cells
|
180
|
+
@attributes.each { |ask| add_cell(@object, ask) }
|
181
|
+
if @args.empty?
|
182
|
+
TableBuilder.default_actions.each { |action| add_cell(@object, action, :action) }
|
183
|
+
else
|
184
|
+
get_column_actions.each { |action| add_cell(@object, action, :action) }
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def get_column_actions
|
189
|
+
if @column_options.has_key? :actions
|
190
|
+
if @column_options[:actions].is_a?(Array)
|
191
|
+
return @column_options[:actions]
|
192
|
+
else
|
193
|
+
return [ @column_options[:actions] ]
|
194
|
+
end
|
195
|
+
end
|
196
|
+
[]
|
197
|
+
end
|
198
|
+
|
199
|
+
def get_columns
|
200
|
+
if @column_options.has_key? :only
|
201
|
+
return @column_options[:only] if @column_options[:only].is_a?(Array)
|
202
|
+
[ @column_options[:only] ]
|
203
|
+
elsif @column_options.has_key? :except
|
204
|
+
return content_columns - @column_options[:except] if @column_options[:except].is_a?(Array)
|
205
|
+
content_columns - [ @column_options[:except] ]
|
206
|
+
else
|
207
|
+
content_columns
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
## Format Cells
|
215
|
+
##
|
216
|
+
##
|
217
|
+
##
|
218
|
+
|
219
|
+
class FormatCell < FormatLine
|
220
|
+
|
221
|
+
def initialize(object, args, type = nil, block = nil)
|
222
|
+
@object, @type, @block = object, type, block
|
223
|
+
if args.is_a? Array
|
224
|
+
@options, @html_options = get_cell_and_html_options( args.extract_options! )
|
225
|
+
@ask = args.first
|
226
|
+
if @ask.nil? and @options.has_key?(:action)
|
227
|
+
@type = :action
|
228
|
+
@ask = @options[:action]
|
229
|
+
end
|
230
|
+
else
|
231
|
+
@ask = args
|
232
|
+
end
|
233
|
+
set_default_options
|
234
|
+
@can_sort = true if @options and @options[:sort] and can_sort_column?(@ask)
|
235
|
+
end
|
236
|
+
|
237
|
+
def render_cell_tbody
|
238
|
+
if @type == :action
|
239
|
+
cell_value = action_link_to(@ask)
|
240
|
+
elsif @ask
|
241
|
+
cell_value = (@ask.is_a?(Symbol)) ? format_cell_value(@object[@ask], @ask) : format_cell_value(@ask)
|
242
|
+
else
|
243
|
+
cell_value = @block
|
244
|
+
end
|
245
|
+
cell_value = action_link_to(@options[:action], cell_value) if @type != :action and @options.has_key?(:action)
|
246
|
+
content_tag(:td, cell_value, @html_options)
|
247
|
+
end
|
248
|
+
|
249
|
+
def render_cell_thead
|
250
|
+
if @ask
|
251
|
+
cell_value = (@ask.is_a?(Symbol)) ? I18n.t(@ask, {}, true) : @ask
|
252
|
+
else
|
253
|
+
cell_value = @block
|
254
|
+
end
|
255
|
+
if @can_sort and @options[:sort]
|
256
|
+
@html_options.merge!(:class => "#{@html_options[:class]} #{sorting_html_class}".strip)
|
257
|
+
content_tag(:th, sort_link_to(cell_value), @html_options)
|
258
|
+
else
|
259
|
+
content_tag(:th, cell_value, @html_options)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
private
|
264
|
+
|
265
|
+
def get_cell_and_html_options(options)
|
266
|
+
return options, options.delete(:html) || {}
|
267
|
+
end
|
268
|
+
|
269
|
+
def set_default_options
|
270
|
+
@html_options = {} unless defined? @html_options
|
271
|
+
@options = {} unless defined? @options
|
272
|
+
@options[:sort] = @@options[:sort] or true if !@options.has_key? :sort
|
273
|
+
end
|
274
|
+
|
275
|
+
def action_link_to(action, block = nil)
|
276
|
+
object_or_array = @@object_or_array.clone
|
277
|
+
object_or_array.push @object
|
278
|
+
return case action.to_sym
|
279
|
+
when :delete
|
280
|
+
create_link_to(block || I18n.t(:delete), object_or_array, @@options[:link_remote], :delete, I18n.t(:confirm_delete))
|
281
|
+
when :show
|
282
|
+
create_link_to(block || I18n.t(:show), object_or_array, @@options[:link_remote])
|
283
|
+
else
|
284
|
+
object_or_array.insert(0, action)
|
285
|
+
create_link_to(block || I18n.t(@ask), object_or_array, @@options[:link_remote])
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
def sort_link_to(name)
|
290
|
+
create_link_to(name, sort_url, @@options[:sort_remote])
|
291
|
+
end
|
292
|
+
|
293
|
+
def create_link_to(block, url, remote, method = nil, confirm = nil)
|
294
|
+
if remote and Tools::rails3?
|
295
|
+
return @@template.link_to(block, url, :method => method, :confirm => confirm, :remote => true)
|
296
|
+
elsif remote
|
297
|
+
method = :get if method.nil?
|
298
|
+
return @@template.link_to_remote(block, { :url => url, :method => method, :confirm => confirm })
|
299
|
+
end
|
300
|
+
@@template.link_to(block, url, :method => method, :confirm => confirm)
|
301
|
+
end
|
302
|
+
|
303
|
+
def sorting_html_class
|
304
|
+
return TableBuilder.html_sorting_class.first if current_sorting.nil?
|
305
|
+
return TableBuilder.html_sorting_class.second if current_sorting == :asc
|
306
|
+
TableBuilder.html_sorting_class.third
|
307
|
+
end
|
308
|
+
|
309
|
+
def sort_url
|
310
|
+
url_params = @@params.clone
|
311
|
+
if url_params.has_key? TableBuilder.params_sort_table
|
312
|
+
if url_params[TableBuilder.params_sort_table].has_key? @ask
|
313
|
+
url_params[TableBuilder.params_sort_table][@ask] = inverse_sorting
|
314
|
+
return @@template.url_for(url_params)
|
315
|
+
end
|
316
|
+
url_params[TableBuilder.params_sort_table].delete @ask
|
317
|
+
end
|
318
|
+
url_params[TableBuilder.params_sort_table] = { @ask => :asc }
|
319
|
+
@@template.url_for(url_params)
|
320
|
+
end
|
321
|
+
|
322
|
+
def current_sorting
|
323
|
+
if @@params.has_key? TableBuilder.params_sort_table and @@params[TableBuilder.params_sort_table].has_key? @ask
|
324
|
+
return @@params[TableBuilder.params_sort_table][@ask].to_sym
|
325
|
+
end
|
326
|
+
nil
|
327
|
+
end
|
328
|
+
|
329
|
+
def inverse_sorting
|
330
|
+
return :asc if current_sorting.nil?
|
331
|
+
return :desc if current_sorting == :asc
|
332
|
+
:asc
|
333
|
+
end
|
334
|
+
|
335
|
+
def format_cell_value(value, attribute = nil)
|
336
|
+
unless (ret_value = format_cell_value_as_ask(value)).nil?
|
337
|
+
return ret_value
|
338
|
+
end
|
339
|
+
format_cell_value_as_type(value, attribute)
|
340
|
+
end
|
341
|
+
|
342
|
+
def format_cell_value_as_ask(value)
|
343
|
+
return nil if !@options or @options.empty? or !@options.has_key?(:as)
|
344
|
+
return case @options[:as]
|
345
|
+
when :date then ::I18n.l(value.to_date, :format => @options[:format] || TableBuilder.i18n_default_format_date)
|
346
|
+
when :time then ::I18n.l(value.to_datetime, :format => @options[:format] || TableBuilder.i18n_default_format_date)
|
347
|
+
when :currency then number_to_currency(value)
|
348
|
+
else nil
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def format_cell_value_as_type(value, attribute)
|
353
|
+
if value.is_a?(Time) or value.is_a?(Date)
|
354
|
+
return ::I18n.l(value, :format => @options[:format] || TableBuilder.i18n_default_format_date)
|
355
|
+
elsif TableBuilder.currency_columns.include?(attribute)
|
356
|
+
return number_to_currency(value)
|
357
|
+
elsif value.is_a?(TrueClass)
|
358
|
+
return TableBuilder.default_boolean.first
|
359
|
+
elsif value.is_a?(FalseClass)
|
360
|
+
return TableBuilder.default_boolean.second
|
361
|
+
end
|
362
|
+
value
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
end
|