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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e3067a61bdc457c0ed1ce15521f79adc1fbddc02
4
- data.tar.gz: 17b710c74fc8587c8625497483661cf366cf2816
3
+ metadata.gz: 02300b6137cd03b63ebfef375d7d9de7402a8892
4
+ data.tar.gz: d353af7ed05239dca230b63825034ea30d74430e
5
5
  SHA512:
6
- metadata.gz: 45971725107ac1af7adde1dd5b21e209e8f17de1a09746d8f6ef129feb61fdccae846cb9b03da28a5b935171c8cc7737e5af12e58f7a214c27212e30e233f758
7
- data.tar.gz: e7d1d46c071f5ef0f668a0a984aa4533482829eee38303a234ddc4eb9b05adcc472b8065d753d0185b28eff9376da877b110b371ebf0f91af4883d393d37d2da
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
- * `#build!`: Creates ActiveRecord instances. Also supports FactoryGirl.
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 `#build!`:
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 field value.
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
- TODO: Pending API overhaul.
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.build! table, Users
144
+ Chop.create! table, Users
127
145
  Chop.diff! table, "table"
128
146
  Chop.fill_in! table
129
147
  ```
data/lib/chop.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "chop/version"
2
- require "chop/builder"
2
+ require "chop/create"
3
3
  require "chop/table"
4
4
  require "chop/definition_list"
5
5
  require "chop/unordered_list"
@@ -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 Builder < Struct.new(:table, :klass, :block)
6
- def self.build! table, klass, &block
7
- new(table, klass, block).build!
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 build! cucumber_table = table
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)
@@ -1,7 +1,11 @@
1
- require "chop/base"
1
+ require "chop/diff"
2
2
 
3
3
  module Chop
4
- class DefinitionList < Base
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
@@ -1,7 +1,7 @@
1
1
  module Chop
2
2
  module DSL
3
3
  def create! table, klass, &block
4
- Builder.build! table, klass, &block
4
+ Create.create! table, klass, &block
5
5
  end
6
6
 
7
7
  def diff! selector, table, session: Capybara.current_session, &block
data/lib/chop/table.rb CHANGED
@@ -1,24 +1,10 @@
1
- require "chop/base"
1
+ require "chop/diff"
2
2
 
3
3
  module Chop
4
- class Table < Base
5
- private
6
-
7
- def default_selector
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
 
@@ -1,24 +1,10 @@
1
- require "chop/base"
1
+ require "chop/diff"
2
2
 
3
3
  module Chop
4
- class UnorderedList < Base
5
- private
6
-
7
- def default_selector
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
@@ -1,3 +1,3 @@
1
1
  module Chop
2
- VERSION = "0.13.2"
2
+ VERSION = "0.14.0"
3
3
  end
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.13.2
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-17 00:00:00.000000000 Z
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/base.rb
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