tabletastic 0.1.0 → 0.1.1
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/README.rdoc +24 -5
- data/VERSION +1 -1
- data/lib/tabletastic.rb +104 -26
- data/spec/tabletastic_spec.rb +78 -1
- data/tabletastic.gemspec +2 -2
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -126,11 +126,10 @@ will produce html like:
|
|
126
126
|
For even greater flexibility, you can pass +data+ a block:
|
127
127
|
|
128
128
|
<% table_for(@posts) do |t| %>
|
129
|
-
<% t.data do %>
|
129
|
+
<% t.data :actions => :all do %>
|
130
130
|
<%= t.cell(:title, :cell_html => {:class => "titlestring"}) %>
|
131
131
|
<%= t.cell(:body, :heading => "Content") {|p| truncate(p.body, 30)} %>
|
132
132
|
<%= t.cell(:author) {|p| p.author && link_to(p.author.name, p.author) } %>
|
133
|
-
<%= t.cell(:edit, :heading => '') {|p| link_to "Edit", edit_post_path(p) } %>
|
134
133
|
<% end -%>
|
135
134
|
<% end %>
|
136
135
|
|
@@ -143,6 +142,8 @@ will product html like:
|
|
143
142
|
<th>Content</th>
|
144
143
|
<th>Author</th>
|
145
144
|
<th></th>
|
145
|
+
<th></th>
|
146
|
+
<th></th>
|
146
147
|
</tr>
|
147
148
|
</thead>
|
148
149
|
<tbody>
|
@@ -152,9 +153,15 @@ will product html like:
|
|
152
153
|
<td>
|
153
154
|
<a href="/authors/1">Jim Bean</a>
|
154
155
|
</td>
|
155
|
-
<td>
|
156
|
+
<td class="actions show_link">
|
157
|
+
<a href="/posts/1">Show</a>
|
158
|
+
</td>
|
159
|
+
<td class="actions edit_link">
|
156
160
|
<a href="/posts/1/edit">Edit</a>
|
157
161
|
</td>
|
162
|
+
<td class="actions destroy_link">
|
163
|
+
<a href="/posts/1/edit">Destroy</a> <!-- inline javascript omitted -->
|
164
|
+
</td>
|
158
165
|
</tr>
|
159
166
|
<tr class="post even" id="post_2">
|
160
167
|
<td class="titlestring">Second Post</td>
|
@@ -162,17 +169,29 @@ will product html like:
|
|
162
169
|
<td>
|
163
170
|
<a href="/authors/2">Jack Daniels</a>
|
164
171
|
</td>
|
165
|
-
<td>
|
172
|
+
<td class="actions show_link">
|
173
|
+
<a href="/posts/2">Show</a>
|
174
|
+
</td>
|
175
|
+
<td class="actions edit_link">
|
166
176
|
<a href="/posts/2/edit">Edit</a>
|
167
177
|
</td>
|
178
|
+
<td class="actions destroy_link">
|
179
|
+
<a href="/posts/2/edit">Destroy</a> <!-- inline javascript omitted -->
|
180
|
+
</td>
|
168
181
|
</tr>
|
169
182
|
<tr class="post odd" id="post_3">
|
170
183
|
<td class="titlestring">Something else</td>
|
171
184
|
<td>Blah!</td>
|
172
185
|
<td></td>
|
173
|
-
<td>
|
186
|
+
<td class="actions show_link">
|
187
|
+
<a href="/posts/3">Show</a>
|
188
|
+
</td>
|
189
|
+
<td class="actions edit_link">
|
174
190
|
<a href="/posts/3/edit">Edit</a>
|
175
191
|
</td>
|
192
|
+
<td class="actions destroy_link">
|
193
|
+
<a href="/posts/3/edit">Destroy</a> <!-- inline javascript omitted -->
|
194
|
+
</td>
|
176
195
|
</tr>
|
177
196
|
</tbody>
|
178
197
|
</table>
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/lib/tabletastic.rb
CHANGED
@@ -2,40 +2,85 @@ module Tabletastic
|
|
2
2
|
|
3
3
|
# returns and outputs a table for the given active record collection
|
4
4
|
def table_for(collection, *args)
|
5
|
+
klass = default_class_for(collection)
|
5
6
|
options = args.extract_options!
|
6
7
|
options[:html] ||= {}
|
7
|
-
options[:html][:id] ||= get_id_for(
|
8
|
+
options[:html][:id] ||= get_id_for(klass)
|
8
9
|
concat(tag(:table, options[:html], true))
|
9
|
-
yield TableBuilder.new(collection, self)
|
10
|
+
yield TableBuilder.new(collection, klass, self)
|
10
11
|
concat("</table>")
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
private
|
15
|
+
# Finds the class representing the objects within the collection
|
16
|
+
def default_class_for(collection)
|
17
|
+
if collection.respond_to?(:proxy_reflection)
|
18
|
+
collection.proxy_reflection.klass
|
19
|
+
elsif !collection.empty?
|
20
|
+
collection.first.class
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_id_for(klass)
|
25
|
+
klass.to_s.tableize
|
15
26
|
end
|
16
27
|
|
17
28
|
class TableBuilder
|
18
29
|
@@association_methods = %w[display_name full_name name title username login value to_s]
|
19
30
|
attr_accessor :field_labels
|
31
|
+
attr_reader :collection, :klass
|
20
32
|
|
21
|
-
def initialize(collection, template)
|
22
|
-
@collection, @template = collection, template
|
33
|
+
def initialize(collection, klass, template)
|
34
|
+
@collection, @klass, @template = collection, klass, template
|
23
35
|
end
|
24
36
|
|
25
37
|
# builds up the fields that the table will include,
|
26
38
|
# returns table head and body with all data
|
27
|
-
|
39
|
+
#
|
40
|
+
# Can be used one of three ways:
|
41
|
+
#
|
42
|
+
# * Alone, which will try to detect all content columns on the resource
|
43
|
+
# * With an array of methods to call on each element in the collection
|
44
|
+
# * With a block, which assumes you will use +cell+ method to build up
|
45
|
+
# the table
|
46
|
+
#
|
47
|
+
#
|
48
|
+
def data(*args, &block) # :yields: tablebody
|
49
|
+
options = args.extract_options!
|
28
50
|
if block_given?
|
29
51
|
yield self
|
52
|
+
action_cells(options[:actions])
|
30
53
|
@template.concat(head)
|
31
54
|
@template.concat(body)
|
32
55
|
else
|
33
|
-
@fields = args
|
56
|
+
@fields = args.empty? ? fields : args
|
34
57
|
@field_labels = fields.map { |f| f.to_s.humanize }
|
58
|
+
action_cells(options[:actions])
|
35
59
|
[head, body].join("")
|
36
60
|
end
|
37
61
|
end
|
38
62
|
|
63
|
+
# individually specify a column, which will build up the header,
|
64
|
+
# and method or block to call on each resource in the array
|
65
|
+
#
|
66
|
+
# Should always be called within the block of +data+
|
67
|
+
#
|
68
|
+
# For example:
|
69
|
+
#
|
70
|
+
# t.cell :blah
|
71
|
+
#
|
72
|
+
# will simply call +blah+ on each resource
|
73
|
+
#
|
74
|
+
# You can also provide a block, which allows for other helpers
|
75
|
+
# or custom formatting. Since by default erb will just call +to_s+
|
76
|
+
# on an any element output, you can more greatly control the output:
|
77
|
+
#
|
78
|
+
# t.cell(:price) {|resource| number_to_currency(resource)}
|
79
|
+
#
|
80
|
+
# would output something like:
|
81
|
+
#
|
82
|
+
# <td>$1.50</td>
|
83
|
+
#
|
39
84
|
def cell(*args, &block)
|
40
85
|
options = args.extract_options!
|
41
86
|
@field_labels ||= []
|
@@ -43,12 +88,16 @@ module Tabletastic
|
|
43
88
|
|
44
89
|
method_or_attribute = args.first.to_sym
|
45
90
|
|
91
|
+
method_or_attribute_or_proc = if block_given?
|
92
|
+
block.to_proc
|
93
|
+
else
|
94
|
+
method_or_attribute
|
95
|
+
end
|
96
|
+
|
46
97
|
if cell_html = options.delete(:cell_html)
|
47
|
-
@fields << [
|
48
|
-
elsif block_given?
|
49
|
-
@fields << block.to_proc
|
98
|
+
@fields << [method_or_attribute_or_proc, cell_html]
|
50
99
|
else
|
51
|
-
@fields <<
|
100
|
+
@fields << method_or_attribute_or_proc
|
52
101
|
end
|
53
102
|
|
54
103
|
if heading = options.delete(:heading)
|
@@ -56,8 +105,9 @@ module Tabletastic
|
|
56
105
|
else
|
57
106
|
@field_labels << method_or_attribute.to_s.humanize
|
58
107
|
end
|
59
|
-
|
60
|
-
|
108
|
+
# Since this will likely be called with <%= %> (aka 'concat'), explicitly return an empty string
|
109
|
+
# This suppresses unwanted output
|
110
|
+
return ""
|
61
111
|
end
|
62
112
|
|
63
113
|
def head
|
@@ -85,12 +135,12 @@ module Tabletastic
|
|
85
135
|
@collection.inject("") do |rows, record|
|
86
136
|
rowclass = @template.cycle("odd","even")
|
87
137
|
rows += @template.content_tag_for(:tr, record, :class => rowclass) do
|
88
|
-
|
138
|
+
cells_for_row(record)
|
89
139
|
end
|
90
140
|
end
|
91
141
|
end
|
92
142
|
|
93
|
-
def
|
143
|
+
def cells_for_row(record)
|
94
144
|
fields.inject("") do |cells, field_or_array|
|
95
145
|
field = field_or_array
|
96
146
|
if field_or_array.is_a?(Array)
|
@@ -113,9 +163,34 @@ module Tabletastic
|
|
113
163
|
result.send(to_string) if to_string
|
114
164
|
end
|
115
165
|
|
166
|
+
# Used internally to build up cells for common CRUD actions
|
167
|
+
def action_cells(actions)
|
168
|
+
return if actions.blank?
|
169
|
+
actions = [actions] if !actions.respond_to?(:each)
|
170
|
+
actions = [:show, :edit, :destroy] if actions == [:all]
|
171
|
+
actions.each do |action|
|
172
|
+
action_link(action.to_sym)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Dynamically builds links for the action
|
177
|
+
def action_link(action)
|
178
|
+
html_class = "actions #{action.to_s}_link"
|
179
|
+
self.cell(action, :heading => "", :cell_html => {:class => html_class}) do |resource|
|
180
|
+
case action
|
181
|
+
when :show
|
182
|
+
@template.link_to("Show", resource)
|
183
|
+
when :edit
|
184
|
+
@template.link_to("Edit", @template.polymorphic_path(resource, :action => :edit))
|
185
|
+
when :destroy
|
186
|
+
@template.link_to("Destroy", resource, :method => :delete)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
116
191
|
def fields
|
117
192
|
return @fields if defined?(@fields)
|
118
|
-
@fields = @collection.empty? ? [] :
|
193
|
+
@fields = @collection.empty? ? [] : active_record_fields
|
119
194
|
end
|
120
195
|
|
121
196
|
protected
|
@@ -125,13 +200,14 @@ module Tabletastic
|
|
125
200
|
end
|
126
201
|
|
127
202
|
|
128
|
-
def
|
203
|
+
def active_record_fields
|
204
|
+
return [] if klass.blank?
|
129
205
|
# normal content columns
|
130
|
-
fields =
|
206
|
+
fields = klass.content_columns.map(&:name)
|
131
207
|
|
132
208
|
# active record associations
|
133
|
-
|
134
|
-
|
209
|
+
if klass.respond_to?(:reflect_on_all_associations)
|
210
|
+
associations = klass.reflect_on_all_associations(:belongs_to)
|
135
211
|
associations = associations.map(&:name)
|
136
212
|
fields += associations
|
137
213
|
end
|
@@ -141,16 +217,18 @@ module Tabletastic
|
|
141
217
|
fields = fields.map(&:to_sym)
|
142
218
|
end
|
143
219
|
|
220
|
+
def content_tag(name, content = nil, options = nil, escape = true, &block)
|
221
|
+
@template.content_tag(name, content, options, escape, &block)
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
144
226
|
def send_or_call(object, duck)
|
145
|
-
if duck.
|
227
|
+
if duck.respond_to?(:call)
|
146
228
|
duck.call(object)
|
147
229
|
else
|
148
230
|
object.send(duck)
|
149
231
|
end
|
150
232
|
end
|
151
|
-
|
152
|
-
def content_tag(name, content = nil, options = nil, escape = true, &block)
|
153
|
-
@template.content_tag(name, content, options, escape, &block)
|
154
|
-
end
|
155
233
|
end
|
156
234
|
end
|
data/spec/tabletastic_spec.rb
CHANGED
@@ -129,6 +129,57 @@ describe "Tabletastic#table_for" do
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
+
context "with options[:actions]" do
|
133
|
+
it "includes path to post for :show" do
|
134
|
+
table_for(@posts) do |t|
|
135
|
+
concat(t.data(:actions => :show))
|
136
|
+
end
|
137
|
+
output_buffer.should have_table_with_tag("a[@href=/posts/#{@post.id}]")
|
138
|
+
output_buffer.should have_table_with_tag("th", "")
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should have a cell with default class 'actions' and the action name" do
|
142
|
+
table_for(@posts) do |t|
|
143
|
+
concat(t.data(:actions => :show))
|
144
|
+
end
|
145
|
+
output_buffer.should have_tag("td.actions.show_link") do |td|
|
146
|
+
td.should have_tag("a")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
it "includes path to post for :edit" do
|
151
|
+
table_for(@posts) do |t|
|
152
|
+
concat(t.data(:actions => :edit))
|
153
|
+
end
|
154
|
+
output_buffer.should have_tag("a[@href=/posts/#{@post.id}/edit]", "Edit")
|
155
|
+
end
|
156
|
+
|
157
|
+
it "includes path to post for :destroy" do
|
158
|
+
table_for(@posts) do |t|
|
159
|
+
concat(t.data(:actions => :destroy))
|
160
|
+
end
|
161
|
+
output_buffer.should have_table_with_tag("a[@href=/posts/#{@post.id}]", "Destroy")
|
162
|
+
output_buffer.should have_table_with_tag("th", "")
|
163
|
+
end
|
164
|
+
|
165
|
+
it "includes path to post for :show and :edit" do
|
166
|
+
table_for(@posts) do |t|
|
167
|
+
concat(t.data(:actions => [:show, :edit]))
|
168
|
+
end
|
169
|
+
output_buffer.should have_tag("td:nth-child(3) a[@href=/posts/#{@post.id}]", "Show")
|
170
|
+
output_buffer.should have_tag("td:nth-child(4) a[@href=/posts/#{@post.id}/edit]", "Edit")
|
171
|
+
end
|
172
|
+
|
173
|
+
it "includes path to post for :all" do
|
174
|
+
table_for(@posts) do |t|
|
175
|
+
concat(t.data(:actions => :all))
|
176
|
+
end
|
177
|
+
output_buffer.should have_tag("td:nth-child(3) a[@href=/posts/#{@post.id}]", "Show")
|
178
|
+
output_buffer.should have_tag("td:nth-child(4) a[@href=/posts/#{@post.id}/edit]", "Edit")
|
179
|
+
output_buffer.should have_tag("td:nth-child(5) a[@href=/posts/#{@post.id}]", "Destroy")
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
132
183
|
context "with a list of attributes" do
|
133
184
|
before do
|
134
185
|
table_for(@posts) do |t|
|
@@ -142,6 +193,20 @@ describe "Tabletastic#table_for" do
|
|
142
193
|
output_buffer.should_not have_table_with_tag("th", "Body")
|
143
194
|
end
|
144
195
|
end
|
196
|
+
|
197
|
+
context "with a list of attributes and options[:actions]" do
|
198
|
+
it "includes path to post for :show" do
|
199
|
+
table_for(@posts) do |t|
|
200
|
+
concat(t.data(:title, :created_at, :actions => :show))
|
201
|
+
end
|
202
|
+
output_buffer.should have_tag("th:nth-child(1)", "Title")
|
203
|
+
output_buffer.should have_tag("th:nth-child(2)", "Created at")
|
204
|
+
output_buffer.should have_tag("th:nth-child(3)", "")
|
205
|
+
output_buffer.should_not have_tag("th", "Body")
|
206
|
+
|
207
|
+
output_buffer.should have_tag("td:nth-child(3) a[@href=/posts/#{@post.id}]")
|
208
|
+
end
|
209
|
+
end
|
145
210
|
end
|
146
211
|
|
147
212
|
context "with a block" do
|
@@ -205,6 +270,18 @@ describe "Tabletastic#table_for" do
|
|
205
270
|
end
|
206
271
|
end
|
207
272
|
|
273
|
+
context "with options[:actions]" do
|
274
|
+
it "includes path to post for :show" do
|
275
|
+
table_for(@posts) do |t|
|
276
|
+
t.data(:actions => :show) do
|
277
|
+
concat(t.cell(:title))
|
278
|
+
concat(t.cell(:body))
|
279
|
+
end
|
280
|
+
end
|
281
|
+
output_buffer.should have_table_with_tag("td:nth-child(3) a[@href=/posts/#{@post.id}]")
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
208
285
|
context "and normal/association columns" do
|
209
286
|
before do
|
210
287
|
::Post.stub!(:reflect_on_all_associations).with(:belongs_to).and_return([@mock_reflection_belongs_to_author])
|
@@ -236,7 +313,7 @@ describe TableBuilder do
|
|
236
313
|
mock_everything
|
237
314
|
::Post.stub!(:content_columns).and_return([mock('column', :name => 'title'), mock('column', :name => 'body'), mock('column', :name => 'created_at')])
|
238
315
|
@posts = [@post, Post.new]
|
239
|
-
@builder = TableBuilder.new(@posts, nil)
|
316
|
+
@builder = TableBuilder.new(@posts, ::Post, nil)
|
240
317
|
end
|
241
318
|
|
242
319
|
it "should detect attributes" do
|
data/tabletastic.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{tabletastic}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Joshua Davey"]
|
12
|
-
s.date = %q{2009-11-
|
12
|
+
s.date = %q{2009-11-22}
|
13
13
|
s.description = %q{A table builder for active record collections that produces semantically rich and accessible markup}
|
14
14
|
s.email = %q{josh@joshuadavey.com}
|
15
15
|
s.extra_rdoc_files = [
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tabletastic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Davey
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-22 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|