chop 0.13.2 → 0.14.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.
- checksums.yaml +4 -4
- data/README.md +24 -6
- data/lib/chop.rb +1 -1
- data/lib/chop/{builder.rb → create.rb} +4 -4
- data/lib/chop/definition_list.rb +6 -20
- data/lib/chop/diff.rb +168 -0
- data/lib/chop/dsl.rb +1 -1
- data/lib/chop/table.rb +5 -19
- data/lib/chop/unordered_list.rb +5 -19
- data/lib/chop/version.rb +1 -1
- metadata +4 -4
- data/lib/chop/base.rb +0 -95
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02300b6137cd03b63ebfef375d7d9de7402a8892
|
4
|
+
data.tar.gz: d353af7ed05239dca230b63825034ea30d74430e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cb5dfb4be29ef2ca2e548b37f2176c8a3e7da2b83e3a985aa9d3f7a53d9c7b92a74d293ba567a995f5055b3824babecbb83f143a32a9d1ca52cb8169642d17a2
|
7
|
+
data.tar.gz: 07787e5e960bb7532855d46fbfbf363e855e2c0814b9fb56a1afcb87cb65440e239a679fc13acf31a0cb08ea6b3e12e290b5ab8eac959c3ddae860ca4cc6b968
|
data/README.md
CHANGED
@@ -16,13 +16,13 @@ end
|
|
16
16
|
|
17
17
|
Chop monkeypatches Cucumber tables with three new methods:
|
18
18
|
|
19
|
-
* `#
|
19
|
+
* `#create!`: Creates ActiveRecord instances. Also supports FactoryGirl.
|
20
20
|
* `#diff!`: Enhances existing method to also accept a CSS selector. Currently supports diffing `<table>`, `<dl>`, and `<ul>`.
|
21
21
|
* `#fill_in!`: Fills in a form on the current page.
|
22
22
|
|
23
23
|
All these methods accept blocks for customization.
|
24
24
|
|
25
|
-
### Block methods for `#
|
25
|
+
### Block methods for `#create!`:
|
26
26
|
|
27
27
|
Transform the attributes hash derived from the table before passing to `ActiveRecord.create!`.
|
28
28
|
|
@@ -36,12 +36,28 @@ High-level declarative transformations:
|
|
36
36
|
* `#rename`: Renames one or more fields.
|
37
37
|
|
38
38
|
All these methods are implemented in terms of the following low-level methods, useful for when you need more control over the transformation:
|
39
|
-
* `#field`: performs transformations on a specific
|
39
|
+
* `#field`: performs transformations on a specific oield value.
|
40
40
|
* `#transformation`: performs transformations on the attributes hash.
|
41
41
|
|
42
42
|
### Block methods for `#diff!`:
|
43
43
|
|
44
|
-
|
44
|
+
Transform the table of Capybara nodes before converting them to text and passing to `diff!`.
|
45
|
+
|
46
|
+
Overide Capybara finders:
|
47
|
+
* `#rows`: overrides existing default row finder.
|
48
|
+
* `#cells`: overrides existing default cell finder.
|
49
|
+
* `#text`: overrides existing default text finder.
|
50
|
+
* `#allow_not_found`: diffing against a missing element will diff against `[[]]` instead of raising an exception.
|
51
|
+
|
52
|
+
High-level declarative transformations:
|
53
|
+
* `#image`: Replaces the specified cell with the alt text of the first image within it.
|
54
|
+
|
55
|
+
All these methods are implemented in terms of the following low-level methods, useful for when you need more control over the transformation:
|
56
|
+
* `#header`: add or transform the table header, depending on block arity.
|
57
|
+
* `#header(key)`: transform the specified header column, specified either by numeric index, or by hash key.
|
58
|
+
* `#field`: performs transformations on a specific field value.
|
59
|
+
* `#hash_transformation`: performs arbitrary transformations on an array of hashes constructed by assuming a header row. Hash keys are downcased and underscored.
|
60
|
+
* `#transformation`: performs arbitrary transformations on the 2d array of Capybara nodes.
|
45
61
|
|
46
62
|
### Block methods for `#fill_in!`:
|
47
63
|
|
@@ -113,7 +129,9 @@ end
|
|
113
129
|
|
114
130
|
Then /^I should see the following "(.+?)" stories:$/ do |industry_name, table|
|
115
131
|
within "dfn", text: industry_name do
|
116
|
-
table.diff! "table"
|
132
|
+
table.diff! "table" do
|
133
|
+
image :image
|
134
|
+
end
|
117
135
|
end
|
118
136
|
end
|
119
137
|
```
|
@@ -123,7 +141,7 @@ end
|
|
123
141
|
Load `chop` before `cucumber` in your Gemfile, and call the two methods directly on the `Chop` module, passing the cucumber table in as the first argument.
|
124
142
|
|
125
143
|
```ruby
|
126
|
-
Chop.
|
144
|
+
Chop.create! table, Users
|
127
145
|
Chop.diff! table, "table"
|
128
146
|
Chop.fill_in! table
|
129
147
|
```
|
data/lib/chop.rb
CHANGED
@@ -2,9 +2,9 @@ require "active_support/core_ext/string/inflections"
|
|
2
2
|
require "active_support/core_ext/object/blank"
|
3
3
|
|
4
4
|
module Chop
|
5
|
-
class
|
6
|
-
def self.
|
7
|
-
new(table, klass, block).
|
5
|
+
class Create < Struct.new(:table, :klass, :block)
|
6
|
+
def self.create! table, klass, &block
|
7
|
+
new(table, klass, block).create!
|
8
8
|
end
|
9
9
|
|
10
10
|
attr_accessor :transformations
|
@@ -16,7 +16,7 @@ module Chop
|
|
16
16
|
instance_eval &other_block if block_given?
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
19
|
+
def create! cucumber_table = table
|
20
20
|
cucumber_table.hashes.map do |attributes|
|
21
21
|
transformations.each { |transformation| transformation.call(attributes) }
|
22
22
|
if klass.is_a?(Hash)
|
data/lib/chop/definition_list.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
require "chop/
|
1
|
+
require "chop/diff"
|
2
2
|
|
3
3
|
module Chop
|
4
|
-
class DefinitionList <
|
4
|
+
class DefinitionList < Diff
|
5
|
+
self.default_selector = "dl"
|
6
|
+
self.rows_finder = ->(root) { root.all("dfn") }
|
7
|
+
self.cells_finder = ->(row) { row.all("dt,dd") }
|
8
|
+
|
5
9
|
def column index, &block
|
6
10
|
transformation do |raw|
|
7
11
|
raw.map!.with_index do |row, row_index|
|
@@ -21,24 +25,6 @@ module Chop
|
|
21
25
|
raw
|
22
26
|
end
|
23
27
|
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def default_selector
|
28
|
-
"dl"
|
29
|
-
end
|
30
|
-
|
31
|
-
def default_rows_finder
|
32
|
-
Proc.new do |root|
|
33
|
-
root.all("dfn")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def default_cells_finder
|
38
|
-
Proc.new do |row|
|
39
|
-
row.all("dt,dd")
|
40
|
-
end
|
41
|
-
end
|
42
28
|
end
|
43
29
|
|
44
30
|
Dl = DefinitionList
|
data/lib/chop/diff.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
require "active_support/core_ext/string/inflections"
|
2
|
+
require "active_support/core_ext/object/blank"
|
3
|
+
require "active_support/core_ext/class/attribute"
|
4
|
+
|
5
|
+
module Chop
|
6
|
+
class Diff < Struct.new(:selector, :table, :session, :block)
|
7
|
+
def self.diff! selector, table, session: Capybara.current_session, &block
|
8
|
+
new(selector, table, session, block).diff!
|
9
|
+
end
|
10
|
+
|
11
|
+
class_attribute :default_selector, :rows_finder, :cells_finder, :text_finder
|
12
|
+
|
13
|
+
self.rows_finder = -> { raise "Missing rows finder!" }
|
14
|
+
self.cells_finder = -> { raise "Missing cells finder!" }
|
15
|
+
self.text_finder = ->(cell) { cell.text }
|
16
|
+
|
17
|
+
def self.text_or_image_alt_finder
|
18
|
+
->(cell) do
|
19
|
+
text = cell.text
|
20
|
+
if text.blank? && image = cell.first("img")
|
21
|
+
image["alt"]
|
22
|
+
else
|
23
|
+
text
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :header_transformations, :transformations
|
29
|
+
|
30
|
+
def initialize selector = nil, table = nil, session = Capybara.current_session, block = nil, &other_block
|
31
|
+
super
|
32
|
+
self.selector ||= default_selector
|
33
|
+
self.header_transformations = []
|
34
|
+
self.transformations = []
|
35
|
+
instance_eval &block if block.respond_to?(:call)
|
36
|
+
instance_eval &other_block if block_given?
|
37
|
+
end
|
38
|
+
|
39
|
+
def header_transformation &block
|
40
|
+
header_transformations << block
|
41
|
+
end
|
42
|
+
|
43
|
+
def header index=nil, &block
|
44
|
+
if index
|
45
|
+
header_transformation do |row|
|
46
|
+
if index.is_a?(Symbol)
|
47
|
+
index = row.index do |cell|
|
48
|
+
text_finder.call(cell).parameterize.underscore.to_sym == index
|
49
|
+
end
|
50
|
+
end
|
51
|
+
row[index] = yield(row[index])
|
52
|
+
end
|
53
|
+
else
|
54
|
+
if block.arity.zero?
|
55
|
+
@new_header = yield
|
56
|
+
else
|
57
|
+
header_transformation do |row|
|
58
|
+
row.replace yield(row)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def transformation &block
|
65
|
+
transformations << block
|
66
|
+
end
|
67
|
+
|
68
|
+
def hash_transformation &block
|
69
|
+
transformation do |rows|
|
70
|
+
header = rows[0]
|
71
|
+
keys = header.to_a.map { |cell| cell.text.parameterize.underscore.to_sym }
|
72
|
+
body = rows[1..-1]
|
73
|
+
hashes = body.map { |row| Hash[keys.zip(row)] }
|
74
|
+
yield hashes
|
75
|
+
rows.replace [header] + hashes.map(&:values)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def field key
|
80
|
+
hash_transformation do |hashes|
|
81
|
+
hashes.map! do |row|
|
82
|
+
row.merge key => yield(row[key])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def image *keys
|
88
|
+
keys.each do |key|
|
89
|
+
field(key) do |cell|
|
90
|
+
if image = cell.first("img")
|
91
|
+
image["alt"]
|
92
|
+
else
|
93
|
+
cell
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def rows &block
|
100
|
+
self.rows_finder = block
|
101
|
+
end
|
102
|
+
|
103
|
+
def cells &block
|
104
|
+
self.cells_finder = block
|
105
|
+
end
|
106
|
+
|
107
|
+
def text &block
|
108
|
+
self.text_finder = block
|
109
|
+
end
|
110
|
+
|
111
|
+
def allow_not_found
|
112
|
+
@allow_not_found = true
|
113
|
+
end
|
114
|
+
|
115
|
+
def to_a
|
116
|
+
rows = rows_finder.call(root).map { |row| cells_finder.call(row).to_a }
|
117
|
+
rows = normalize(rows)
|
118
|
+
|
119
|
+
header = @new_header ? normalize([@new_header]).first : rows.shift
|
120
|
+
header_transformations.each do |transformation|
|
121
|
+
transformation.call(header)
|
122
|
+
header = normalize([header]).first
|
123
|
+
end
|
124
|
+
|
125
|
+
rows = [header] + rows
|
126
|
+
|
127
|
+
transformations.each do |transformation|
|
128
|
+
transformation.call(rows)
|
129
|
+
rows = normalize(rows)
|
130
|
+
end
|
131
|
+
|
132
|
+
rows.map do |row|
|
133
|
+
row.map { |cell| text_finder.call(cell) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def diff! cucumber_table = table
|
138
|
+
cucumber_table.diff! to_a
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def normalize rows
|
144
|
+
max = rows.map(&:count).max
|
145
|
+
rows.map do |row|
|
146
|
+
row.to_a << "" while row.length < max
|
147
|
+
row.map { |cell| Node(cell) }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def root
|
152
|
+
@root ||= begin
|
153
|
+
session.find(selector)
|
154
|
+
rescue Capybara::ElementNotFound
|
155
|
+
raise unless @allow_not_found
|
156
|
+
Node("")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def Node value
|
161
|
+
if value.respond_to?(:text)
|
162
|
+
value
|
163
|
+
else
|
164
|
+
Capybara::Node::Simple.new(value.to_s)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/chop/dsl.rb
CHANGED
data/lib/chop/table.rb
CHANGED
@@ -1,24 +1,10 @@
|
|
1
|
-
require "chop/
|
1
|
+
require "chop/diff"
|
2
2
|
|
3
3
|
module Chop
|
4
|
-
class Table <
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
"table"
|
9
|
-
end
|
10
|
-
|
11
|
-
def default_rows_finder
|
12
|
-
Proc.new do |root|
|
13
|
-
root.all("tr")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def default_cells_finder
|
18
|
-
Proc.new do |row|
|
19
|
-
row.all("td,th")
|
20
|
-
end
|
21
|
-
end
|
4
|
+
class Table < Diff
|
5
|
+
self.default_selector = "table"
|
6
|
+
self.rows_finder = ->(root) { root.all("tr") }
|
7
|
+
self.cells_finder = ->(row) { row.all("td,th") }
|
22
8
|
end
|
23
9
|
end
|
24
10
|
|
data/lib/chop/unordered_list.rb
CHANGED
@@ -1,24 +1,10 @@
|
|
1
|
-
require "chop/
|
1
|
+
require "chop/diff"
|
2
2
|
|
3
3
|
module Chop
|
4
|
-
class UnorderedList <
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
"ul"
|
9
|
-
end
|
10
|
-
|
11
|
-
def default_rows_finder
|
12
|
-
Proc.new do |root|
|
13
|
-
root.all("li")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def default_cells_finder
|
18
|
-
Proc.new do |row|
|
19
|
-
[row]
|
20
|
-
end
|
21
|
-
end
|
4
|
+
class UnorderedList < Diff
|
5
|
+
self.default_selector = "ul"
|
6
|
+
self.rows_finder = ->(root) { root.all("li") }
|
7
|
+
self.cells_finder = ->(row) { [row] }
|
22
8
|
end
|
23
9
|
|
24
10
|
Ul = UnorderedList
|
data/lib/chop/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Micah Geisel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-03-
|
11
|
+
date: 2017-03-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -126,9 +126,9 @@ files:
|
|
126
126
|
- bin/setup
|
127
127
|
- chop.gemspec
|
128
128
|
- lib/chop.rb
|
129
|
-
- lib/chop/
|
130
|
-
- lib/chop/builder.rb
|
129
|
+
- lib/chop/create.rb
|
131
130
|
- lib/chop/definition_list.rb
|
131
|
+
- lib/chop/diff.rb
|
132
132
|
- lib/chop/dsl.rb
|
133
133
|
- lib/chop/form.rb
|
134
134
|
- lib/chop/table.rb
|
data/lib/chop/base.rb
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
require "active_support/core_ext/object/blank"
|
2
|
-
|
3
|
-
module Chop
|
4
|
-
class Base < Struct.new(:selector, :table, :session, :block)
|
5
|
-
def self.diff! selector, table, session: Capybara.current_session, &block
|
6
|
-
new(selector, table, session, block).diff!
|
7
|
-
end
|
8
|
-
|
9
|
-
attr_accessor :rows_finder
|
10
|
-
attr_accessor :cells_finder
|
11
|
-
attr_accessor :transformations
|
12
|
-
|
13
|
-
def initialize selector = nil, table = nil, session = Capybara.current_session, block = nil, &other_block
|
14
|
-
super
|
15
|
-
self.selector ||= default_selector
|
16
|
-
self.rows_finder = default_rows_finder
|
17
|
-
self.cells_finder = default_cells_finder
|
18
|
-
self.transformations = []
|
19
|
-
instance_eval &block if block.respond_to?(:call)
|
20
|
-
instance_eval &other_block if block_given?
|
21
|
-
end
|
22
|
-
|
23
|
-
def transformation &block
|
24
|
-
transformations << block
|
25
|
-
end
|
26
|
-
|
27
|
-
def normalize
|
28
|
-
transformation do |raw|
|
29
|
-
max = raw.map(&:count).max
|
30
|
-
raw.map! do |row|
|
31
|
-
row << "" while row.length < max
|
32
|
-
row
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def rows &block
|
38
|
-
self.rows_finder = block
|
39
|
-
end
|
40
|
-
|
41
|
-
def cells &block
|
42
|
-
self.cells_finder = block
|
43
|
-
end
|
44
|
-
|
45
|
-
def allow_not_found
|
46
|
-
@allow_not_found = true
|
47
|
-
end
|
48
|
-
|
49
|
-
def to_a
|
50
|
-
results = rows_finder.call(root).map do |row|
|
51
|
-
row_to_text(row)
|
52
|
-
end
|
53
|
-
normalize
|
54
|
-
transformations.each { |transformation| transformation.call(results) }
|
55
|
-
results
|
56
|
-
end
|
57
|
-
|
58
|
-
def diff! cucumber_table = table
|
59
|
-
cucumber_table.diff! to_a
|
60
|
-
end
|
61
|
-
|
62
|
-
def hashes
|
63
|
-
rows = to_a.dup
|
64
|
-
header = rows.shift
|
65
|
-
rows.map do |row|
|
66
|
-
Hash[header.zip(row)]
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def root
|
73
|
-
@root ||= begin
|
74
|
-
session.find(selector)
|
75
|
-
rescue Capybara::ElementNotFound
|
76
|
-
raise unless @allow_not_found
|
77
|
-
Capybara::Node::Simple.new("")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def row_to_text row
|
82
|
-
cells_finder.call(row).map do |cell|
|
83
|
-
cell_to_text(cell)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def cell_to_text cell
|
88
|
-
text = cell.text
|
89
|
-
if text.blank? and image = cell.all("img").first
|
90
|
-
text = image["alt"]
|
91
|
-
end
|
92
|
-
text
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|