table_cloth 0.3.2 → 0.4.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/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
|