table_cloth 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5 -0
- data/Gemfile +1 -3
- data/README.md +15 -13
- data/lib/table_cloth.rb +1 -0
- data/lib/table_cloth/presenter.rb +2 -12
- data/lib/table_cloth/presenters/default.rb +34 -17
- data/lib/table_cloth/version.rb +1 -1
- data/spec/lib/action_view_extension_spec.rb +1 -2
- data/spec/lib/presenter_spec.rb +0 -43
- data/spec/lib/presenters/default_spec.rb +27 -41
- data/spec/support/matchers/element_matchers.rb +11 -2
- data/table_cloth.gemspec +1 -0
- metadata +19 -2
data/CHANGELOG
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -13,7 +13,9 @@ Follow me! [@robertoross](http://twitter.com/robertoross)
|
|
13
13
|
|
14
14
|
Add this line to your application's Gemfile:
|
15
15
|
|
16
|
-
|
16
|
+
```ruby
|
17
|
+
gem 'table_cloth'
|
18
|
+
```
|
17
19
|
|
18
20
|
And then execute:
|
19
21
|
|
@@ -31,7 +33,7 @@ $ rails g table User
|
|
31
33
|
|
32
34
|
It will make this:
|
33
35
|
|
34
|
-
```
|
36
|
+
```ruby
|
35
37
|
class UserTable < TableCloth::Base
|
36
38
|
# Define columns with the #column method
|
37
39
|
# column :name, :email
|
@@ -66,14 +68,14 @@ end
|
|
66
68
|
Go ahead and modify it to suit your needs, pick the columns, conditions, actions, etc...
|
67
69
|
|
68
70
|
In your view, you would then use this code:
|
69
|
-
```
|
70
|
-
<%= simple_table_for @users, with: UserTable %>
|
71
71
|
|
72
|
+
```erb
|
73
|
+
<%= simple_table_for @users, with: UserTable %>
|
72
74
|
```
|
73
75
|
|
74
|
-
|
76
|
+
Or if you want more customization:
|
75
77
|
|
76
|
-
```
|
78
|
+
```erb
|
77
79
|
<%= simple_table_for @users do |t| %>
|
78
80
|
<% t.column :name %>
|
79
81
|
<% t.column :email %>
|
@@ -95,8 +97,9 @@ class ImageColumn < TableCloth::Column
|
|
95
97
|
end
|
96
98
|
```
|
97
99
|
|
98
|
-
In your table
|
99
|
-
|
100
|
+
In your table:
|
101
|
+
|
102
|
+
```erb
|
100
103
|
<%= simple_table_for @users do |table| %>
|
101
104
|
<% table.column :name %>
|
102
105
|
<% table.column :image, using: ImageColumn %>
|
@@ -105,9 +108,9 @@ In your table
|
|
105
108
|
|
106
109
|
## Actions
|
107
110
|
|
108
|
-
A lot of tables have an actions column to give you the full CRUD effect. They can be painful but Table Cloth incorporates a way to easily add them to your definition
|
111
|
+
A lot of tables have an actions column to give you the full CRUD effect. They can be painful but Table Cloth incorporates a way to easily add them to your definition:
|
109
112
|
|
110
|
-
```
|
113
|
+
```ruby
|
111
114
|
class UserTable < TableCloth::Base
|
112
115
|
column :name
|
113
116
|
|
@@ -127,7 +130,7 @@ end
|
|
127
130
|
|
128
131
|
Create an initializer called ```table_cloth.rb```
|
129
132
|
|
130
|
-
|
133
|
+
It should look like this:
|
131
134
|
|
132
135
|
```ruby
|
133
136
|
TableCloth::Configuration.configure do |config|
|
@@ -140,7 +143,7 @@ TableCloth::Configuration.configure do |config|
|
|
140
143
|
end
|
141
144
|
```
|
142
145
|
|
143
|
-
You can also configure specific tables separately
|
146
|
+
You can also configure specific tables separately:
|
144
147
|
|
145
148
|
```ruby
|
146
149
|
class UserTable < TableCloth::Base
|
@@ -182,7 +185,6 @@ class UserTable < TableCloth::Base
|
|
182
185
|
[user.name, {class: "#{user.type}-user"}]
|
183
186
|
end
|
184
187
|
end
|
185
|
-
|
186
188
|
```
|
187
189
|
|
188
190
|
This would render something alow the lines of:
|
data/lib/table_cloth.rb
CHANGED
@@ -12,11 +12,11 @@ module TableCloth
|
|
12
12
|
raise NoMethodError, "You must override the .render method"
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
15
|
+
def thead
|
16
16
|
raise NoMethodError, "You must override the .header method"
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def tbody
|
20
20
|
raise NoMethodError, "You must override the .rows method"
|
21
21
|
end
|
22
22
|
|
@@ -45,16 +45,6 @@ module TableCloth
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
-
def wrapper_tag(type, value=nil, options={}, &block)
|
49
|
-
options = tag_options(type, options)
|
50
|
-
|
51
|
-
if block_given?
|
52
|
-
view_context.content_tag(type, options, &block)
|
53
|
-
else
|
54
|
-
view_context.content_tag(type, value, options)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
48
|
private
|
59
49
|
|
60
50
|
def tag_options(type, options={})
|
@@ -2,24 +2,43 @@ module TableCloth
|
|
2
2
|
module Presenters
|
3
3
|
class Default < ::TableCloth::Presenter
|
4
4
|
def render_table
|
5
|
-
|
6
|
-
|
5
|
+
@render_table ||= ElementFactory::Element.new(:table, tag_options(:table)).tap do |table|
|
6
|
+
table << thead
|
7
|
+
table << tbody
|
7
8
|
end
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
11
|
+
def thead
|
12
|
+
@thead ||= ElementFactory::Element.new(:thead, tag_options(:thead)).tap do |thead|
|
13
|
+
thead << thead_row
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
17
|
+
def tbody
|
18
|
+
@tbody ||= ElementFactory::Element.new(:tbody, tag_options(:tbody)).tap do |tbody|
|
19
|
+
objects.each {|object| tbody << row_for_object(object) }
|
19
20
|
end
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
+
private
|
24
|
+
|
25
|
+
def thead_row
|
26
|
+
@thead_row ||= ElementFactory::Element.new(:tr, tag_options(:tr)).tap do |row|
|
27
|
+
column_names.each do |name|
|
28
|
+
row << ElementFactory::Element.new(:th, tag_options(:th).merge(text: name))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def row_for_object(object)
|
34
|
+
ElementFactory::Element.new(:tr, tag_options(:tr)).tap do |row|
|
35
|
+
columns.each do |column|
|
36
|
+
row << column_for_object(column, object)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def column_for_object(column, object)
|
23
42
|
td_options = column.options[:td_options] || {}
|
24
43
|
value = column.value(object, view_context, table)
|
25
44
|
|
@@ -30,15 +49,13 @@ module TableCloth
|
|
30
49
|
td_options.update(options)
|
31
50
|
end
|
32
51
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
wrapper_tag :thead do
|
38
|
-
wrapper_tag :tr do
|
39
|
-
v.raw column_names.inject("") {|tags, name| tags + wrapper_tag(:th, name) }
|
40
|
-
end
|
52
|
+
if value.html_safe?
|
53
|
+
td_options[:inner_html] = value
|
54
|
+
else
|
55
|
+
td_options[:text] = value
|
41
56
|
end
|
57
|
+
|
58
|
+
ElementFactory::Element.new(:td, tag_options(:td).merge(td_options))
|
42
59
|
end
|
43
60
|
end
|
44
61
|
end
|
data/lib/table_cloth/version.rb
CHANGED
data/spec/lib/presenter_spec.rb
CHANGED
@@ -53,47 +53,4 @@ describe TableCloth::Presenter do
|
|
53
53
|
expected = objects.map {|o| [o.id, o.name, o.email] }
|
54
54
|
expect(subject.rows).to eq(expected)
|
55
55
|
end
|
56
|
-
|
57
|
-
context '.wrapper_tag' do
|
58
|
-
it "creates a tag with a block" do
|
59
|
-
table = subject.wrapper_tag(:table) { "Hello" }
|
60
|
-
element = to_element(table, "table")
|
61
|
-
expect(element).to be_present
|
62
|
-
expect(element.text).to eq("Hello")
|
63
|
-
end
|
64
|
-
|
65
|
-
it "creates a tag without a block" do
|
66
|
-
table = subject.wrapper_tag(:table, "Hello")
|
67
|
-
element = to_element(table, "table")
|
68
|
-
expect(element).to be_present
|
69
|
-
expect(element.text).to eq("Hello")
|
70
|
-
end
|
71
|
-
|
72
|
-
context "config" do
|
73
|
-
let(:config) { double("config", config_for: { class: "table_class" }) }
|
74
|
-
|
75
|
-
it "inherits options from global config" do
|
76
|
-
TableCloth.config.stub config_for: {class: "global_class"}
|
77
|
-
tag = subject.wrapper_tag(:table, "Hello")
|
78
|
-
element = to_element(tag, "table")
|
79
|
-
|
80
|
-
expect(element[:class]).to eq("global_class")
|
81
|
-
end
|
82
|
-
|
83
|
-
it "includes config sent to it" do
|
84
|
-
tag = subject.wrapper_tag(:table, "Hello", {class: "passed_class"})
|
85
|
-
element = to_element(tag, "table")
|
86
|
-
|
87
|
-
expect(element[:class]).to eq("passed_class")
|
88
|
-
end
|
89
|
-
|
90
|
-
it "includes config from the table instance" do
|
91
|
-
dummy_table.stub config: config
|
92
|
-
tag = subject.wrapper_tag(:table, "Hello", {class: "passed_class"})
|
93
|
-
element = to_element(tag, "table")
|
94
|
-
|
95
|
-
expect(element[:class]).to eq("table_class")
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
56
|
end
|
@@ -11,74 +11,60 @@ describe TableCloth::Presenters::Default do
|
|
11
11
|
let(:view_context) { ActionView::Base.new }
|
12
12
|
subject { TableCloth::Presenters::Default.new(objects, dummy_table, view_context) }
|
13
13
|
|
14
|
-
|
14
|
+
describe "#thead" do
|
15
15
|
let(:column_names) { ["Col1", "Col2"] }
|
16
|
+
let(:thead) { Nokogiri::HTML(subject.thead.to_s) }
|
16
17
|
|
17
18
|
before(:each) do
|
18
19
|
subject.stub column_names: column_names
|
19
20
|
end
|
20
21
|
|
21
22
|
it "creates a thead" do
|
22
|
-
expect(
|
23
|
+
expect(thead).to have_tag "thead"
|
23
24
|
end
|
24
25
|
|
25
26
|
it "creates th's" do
|
26
|
-
|
27
|
-
expect(header.css("th").size).to be 2
|
27
|
+
expect(thead.css("th").size).to be 2
|
28
28
|
end
|
29
29
|
|
30
30
|
it "creates th's with the correct text" do
|
31
|
-
|
32
|
-
|
31
|
+
thead.css("th").each_with_index do |th, i|
|
32
|
+
expect(th.text).to eq(column_names[i])
|
33
|
+
end
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
expect(subject.render_rows).to have_tag "tbody"
|
39
|
-
end
|
37
|
+
describe "#render_table" do
|
38
|
+
let(:table) { Nokogiri::HTML(subject.render_table.to_s) }
|
40
39
|
|
41
|
-
it "
|
42
|
-
|
43
|
-
expect(tbody.css('tr').size).to be 3
|
40
|
+
it "renders a table tag" do
|
41
|
+
expect(table).to have_tag "table"
|
44
42
|
end
|
45
43
|
end
|
46
44
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
name: "<script>alert(\"Im in your columns, snatching your main thread.\")</script>"
|
51
|
-
)
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'does not allow unescaped values in columns' do
|
55
|
-
tbody = Nokogiri::HTML(subject.render_rows).at_xpath('//tbody')
|
56
|
-
|
57
|
-
tbody.xpath('//td').each do |td|
|
58
|
-
td.at_xpath('.//script').should_not be_present
|
59
|
-
end
|
45
|
+
describe "#tbody" do
|
46
|
+
it 'creates a tbody' do
|
47
|
+
expect(subject.tbody.to_s).to have_tag "tbody"
|
60
48
|
end
|
61
|
-
end
|
62
49
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
it "td has a class set" do
|
68
|
-
td = Nokogiri::HTML(subject.render_td(column, model)).at_css("td")
|
69
|
-
expect(td[:class]).to eq "email_column"
|
50
|
+
it "creates a row in the tbody" do
|
51
|
+
tbody = Nokogiri::HTML(subject.tbody.to_s)
|
52
|
+
expect(tbody.css('tr').size).to be 3
|
70
53
|
end
|
71
54
|
|
72
|
-
context
|
73
|
-
|
74
|
-
|
55
|
+
context 'escaped values' do
|
56
|
+
let(:objects) do
|
57
|
+
FactoryGirl.build_list(:dummy_model, 1,
|
58
|
+
name: "<script>alert(\"Im in your columns, snatching your main thread.\")</script>"
|
59
|
+
)
|
75
60
|
end
|
76
61
|
|
77
|
-
|
78
|
-
|
62
|
+
it 'does not allow unescaped values in columns' do
|
63
|
+
tbody = Nokogiri::HTML(subject.tbody.to_s).at_xpath('//tbody')
|
79
64
|
|
80
|
-
|
81
|
-
|
65
|
+
tbody.xpath('//td').each do |td|
|
66
|
+
td.at_xpath('.//script').should_not be_present
|
67
|
+
end
|
82
68
|
end
|
83
69
|
end
|
84
70
|
end
|
@@ -1,6 +1,15 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
|
1
3
|
RSpec::Matchers.define :have_tag do |tag|
|
2
4
|
match do |string|
|
3
|
-
document = Nokogiri::HTML(string)
|
4
|
-
!!document.
|
5
|
+
document = Nokogiri::HTML(string.to_s)
|
6
|
+
!!document.at_css(tag)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
RSpec::Matchers.define :have_xpath do |tag|
|
11
|
+
match do |string|
|
12
|
+
document = Nokogiri::HTML(string.to_s)
|
13
|
+
!!document.at_xpath(tag)
|
5
14
|
end
|
6
15
|
end
|
data/table_cloth.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: table_cloth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-05-27 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -177,6 +177,22 @@ dependencies:
|
|
177
177
|
- - <
|
178
178
|
- !ruby/object:Gem::Version
|
179
179
|
version: '4.1'
|
180
|
+
- !ruby/object:Gem::Dependency
|
181
|
+
name: element_factory
|
182
|
+
requirement: !ruby/object:Gem::Requirement
|
183
|
+
none: false
|
184
|
+
requirements:
|
185
|
+
- - ~>
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 0.1.0
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
none: false
|
192
|
+
requirements:
|
193
|
+
- - ~>
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: 0.1.0
|
180
196
|
description: Table Cloth helps you create tables easily.
|
181
197
|
email:
|
182
198
|
- robert@creativequeries.com
|
@@ -187,6 +203,7 @@ files:
|
|
187
203
|
- .gitignore
|
188
204
|
- .rspec
|
189
205
|
- .travis.yml
|
206
|
+
- CHANGELOG
|
190
207
|
- Gemfile
|
191
208
|
- Guardfile
|
192
209
|
- LICENSE.txt
|