chop 0.8.0 → 0.9.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: 5e7cb04d85d09ab8d21ecdc9d07c59972a4daba7
4
- data.tar.gz: 72c5efe46c120810aeadcb6958f870b983a631cd
3
+ metadata.gz: 7fae72c2ccddb7b9d3cf1e2af62273f6e3fa108b
4
+ data.tar.gz: 0b333a8b0dfedf4c1da7ae3d56e01dae37684a70
5
5
  SHA512:
6
- metadata.gz: a72bf7733a5db76a20212921d0f5cbd8f1ab57c7abd2ed6545474e26c96d20faa15330025fb26de45fc5df7a4933e441e669abb16042645f81e58367228ae10f
7
- data.tar.gz: 8d044df36e430cd64032f42917dff0abf449ae76eaa7f1bf2d8151bd816bf176c667b854885ea7028929b10f7189e33e01f6afd678edb3dd882bb034aedeb843
6
+ metadata.gz: 95a559fee2d75b1c7ec85114d55464a11285a582cbefa9b6fce85c5f7c958e58b66bbaaa5fe9d21c72e7d769353aea3f1f6b467ebcfda4816888383c9ec8b791
7
+ data.tar.gz: 1b993d05855c234058edb68cb7164acaa70ff18c40b1693e75371567e4e3c4050ca9410f8d18c71970d1c24e1434deaf380041772ae346acaf79f3be76eeb51b
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
  /tmp/
10
10
  .ruby-version
11
11
  .ruby-gemset
12
+ .byebug_history
@@ -1,4 +1,3 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.2.3
4
- before_install: gem install bundler -v 1.10.6
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in chop.gemspec
4
4
  gemspec
5
+
6
+ gem "byebug"
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Slice and dice your Cucumber tables with ease! Assumes usage of ActiveRecord and Capybara.
4
4
 
5
+ [![Build Status](https://travis-ci.org/botandrose/chop.svg?branch=master)](https://travis-ci.org/botandrose/chop)
6
+
5
7
  ## Installation
6
8
 
7
9
  ```ruby
@@ -14,7 +16,7 @@ end
14
16
 
15
17
  Chop monkeypatches Cucumber tables with two new methods:
16
18
 
17
- * `#build!`: Creates ActiveRecord instances.
19
+ * `#build!`: Creates ActiveRecord instances. Also supports FactoryGirl.
18
20
  * `#diff!`: Enhances existing method to also accept a CSS selector. Currently supports diffing `<table>`, `<dl>`, and `<ul>`.
19
21
 
20
22
  Both these methods accept blocks for customization.
@@ -30,6 +32,7 @@ High-level declarative transformations:
30
32
  * `#has_one/#belongs_to`: Replaces an entity name with that entity. Uses `.find_by_name` by default.
31
33
  * `#has_many`: Replaces a comma-delimited list of entity names with an array of those entities. Uses `.find_by_name` by default.
32
34
  * `#underscore_keys`: Converts all hash keys to underscored versions.
35
+ * `#rename`: Renames one or more fields.
33
36
 
34
37
  All these methods are implemented in terms of the following low-level methods, useful for when you need more control over the transformation:
35
38
  * `#field`: performs transformations on a specific field value.
@@ -45,23 +48,23 @@ TODO: Pending API overhaul.
45
48
  # features/manage_industries.feature
46
49
 
47
50
  Given the following industries exist:
48
- | name | wall_background_file | table_background_file |
49
- | Another industry | wall.jpg | table.jpg |
50
- | Example industry | wall.jpg | table.jpg |
51
+ | name | wall_background | table_background |
52
+ | Another industry | wall.jpg | table.jpg |
53
+ | Example industry | wall.jpg | table.jpg |
51
54
 
52
55
  Given the following stories exist:
53
- | industry | image_file | headline |
54
- | Example industry | example.jpg | Example headline |
55
- | Example industry | example.jpg | Another headline |
56
+ | industry | image | headline |
57
+ | Example industry | example.jpg | Example headline |
58
+ | Example industry | example.jpg | Another headline |
56
59
 
57
60
  Given I am on the home page
58
61
  Then I should see the following industries:
59
62
  | ANOTHER INDUSTRY |
60
63
  | EXAMPLE INDUSTRY |
61
64
  And I should see the following "EXAMPLE INDUSTRY" stories:
62
- | IMAGE | HEADLINE |
63
- | example.jpg | Example headline |
64
- | example.jpg | Another headline |
65
+ | IMAGE | HEADLINE |
66
+ | example.jpg | Example headline |
67
+ | example.jpg | Another headline |
65
68
  ```
66
69
 
67
70
  ```ruby
@@ -69,13 +72,18 @@ And I should see the following "EXAMPLE INDUSTRY" stories:
69
72
 
70
73
  Given "the following industries exist:" do |table|
71
74
  table.create! ConversationTable::Industry do
75
+ rename({
76
+ :wall_background => wall_background_file,
77
+ :table_background => table_background_file,
78
+ })
72
79
  file :wall_background_file, :table_background_file
73
80
  end
74
81
  end
75
82
 
76
83
  Given "the following stories exist:" do |table|
77
- table.create! ConversationTable::Story do
84
+ table.create! factory_girl: "conversation_table/story" do
78
85
  belongs_to :industry, ConversationTable::Industry
86
+ rename :image => :image_file
79
87
  file :image_file
80
88
  end
81
89
  end
@@ -19,7 +19,12 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
+ spec.add_dependency "activerecord"
23
+ spec.add_dependency "cucumber"
24
+ spec.add_dependency "capybara"
25
+
22
26
  spec.add_development_dependency "bundler", "~> 1.10"
23
27
  spec.add_development_dependency "rake", "~> 10.0"
24
28
  spec.add_development_dependency "rspec"
29
+ spec.add_development_dependency "slim"
25
30
  end
@@ -3,34 +3,9 @@ require "chop/builder"
3
3
  require "chop/table"
4
4
  require "chop/definition_list"
5
5
  require "chop/unordered_list"
6
+ require "chop/dsl"
6
7
 
7
8
  module Chop
8
- class << self
9
- def create! table, klass, &block
10
- Builder.build! table, klass, &block
11
- end
12
-
13
- def diff! table, selector, &block
14
- class_name = Capybara.current_session.find(selector).tag_name.capitalize
15
- klass = const_get(class_name)
16
- klass.diff! table, &block
17
- end
18
- end
19
- end
20
-
21
- if defined?(Cucumber::MultilineArgument::DataTable)
22
- Cucumber::MultilineArgument::DataTable.prepend Module.new {
23
- def create! klass, &block
24
- Chop.create! self, klass, &block
25
- end
26
-
27
- def diff! other_table, options={}, &block
28
- if other_table.is_a?(String) && !other_table.include?("|")
29
- Chop.diff! self, other_table, &block
30
- else
31
- super
32
- end
33
- end
34
- }
9
+ extend DSL
35
10
  end
36
11
 
@@ -1,3 +1,6 @@
1
+ require "active_support/core_ext/string/inflections"
2
+ require "active_support/core_ext/object/blank"
3
+
1
4
  module Chop
2
5
  class Builder < Struct.new(:table, :klass, :block)
3
6
  def self.build! table, klass, &block
@@ -6,16 +9,25 @@ module Chop
6
9
 
7
10
  attr_accessor :transformations
8
11
 
9
- def initialize(*)
12
+ def initialize(*, &other_block)
10
13
  super
11
14
  self.transformations = []
12
15
  instance_eval &block if block.respond_to?(:call)
16
+ instance_eval &other_block if block_given?
13
17
  end
14
18
 
15
- def build!
16
- table.hashes.map do |attributes|
19
+ def build! cucumber_table = table
20
+ cucumber_table.hashes.map do |attributes|
17
21
  transformations.each { |transformation| transformation.call(attributes) }
18
- klass.create! attributes
22
+ if klass.is_a?(Hash)
23
+ if factory = klass[:factory_girl]
24
+ FactoryGirl.create factory, attributes
25
+ else
26
+ raise "Unknown building strategy"
27
+ end
28
+ else
29
+ klass.create! attributes
30
+ end
19
31
  end
20
32
  end
21
33
 
@@ -23,6 +35,14 @@ module Chop
23
35
  transformations << block
24
36
  end
25
37
 
38
+ def rename mappings
39
+ transformation do |attributes|
40
+ mappings.each do |old, new|
41
+ attributes[new.to_s] = attributes.delete(old.to_s) if attributes.key?(old.to_s)
42
+ end
43
+ end
44
+ end
45
+
26
46
  def field attribute, default: ""
27
47
  transformation do |attributes|
28
48
  attributes[attribute.to_s] = yield(attributes.fetch(attribute.to_s, default))
@@ -66,7 +86,7 @@ module Chop
66
86
 
67
87
  def has_one key, klass=nil, name_field: :name
68
88
  field key do |name|
69
- klass.find_by!(name_field => name)
89
+ klass.find_by!(name_field => name) if name.present?
70
90
  end
71
91
  end
72
92
  alias_method :belongs_to, :has_one
@@ -1,30 +1,67 @@
1
- require "delegate"
1
+ require "active_support/core_ext/object/blank"
2
2
 
3
3
  module Chop
4
- class DefinitionList < SimpleDelegator
5
- def self.diff! table, &block
6
- new.diff! table
4
+ class DefinitionList < 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
7
  end
8
8
 
9
- def initialize selector = "dl", session: Capybara.current_session
10
- super(session)
11
- @selector = selector
9
+ attr_accessor :transformations
10
+
11
+ def initialize selector = "dl", table = nil, session = Capybara.current_session, block = nil, &other_block
12
+ super
13
+ self.transformations = []
14
+ instance_eval &block if block.respond_to?(:call)
15
+ instance_eval &other_block if block_given?
12
16
  end
13
17
 
14
- def to_a
18
+ def base_to_a
15
19
  rows.collect do |row|
16
20
  row_to_text(row)
17
21
  end
18
22
  end
19
23
 
20
24
  def normalized_to_a
21
- raw = to_a
25
+ raw = base_to_a
22
26
  max = raw.map(&:count).max
23
- raw.select { |row| row.count == max }
27
+ raw.map do |row|
28
+ row << "" while row.length < max
29
+ row
30
+ end
31
+ end
32
+
33
+ def to_a
34
+ results = normalized_to_a
35
+ transformations.each { |transformation| transformation.call(results) }
36
+ results
24
37
  end
25
38
 
26
- def diff! table
27
- table.diff! normalized_to_a
39
+ def transformation &block
40
+ transformations << block
41
+ end
42
+
43
+ def diff! cucumber_table = table
44
+ cucumber_table.diff! to_a
45
+ end
46
+
47
+ def column index, &block
48
+ transformation do |raw|
49
+ raw.map!.with_index do |row, row_index|
50
+ row_element = rows[row_index]
51
+ cell_element = cells(row_element)[index]
52
+ row[index] = block.call(row[index], cell_element, row_element)
53
+ row
54
+ end
55
+ end
56
+ end
57
+
58
+ def header &block
59
+ transformation do |raw|
60
+ new_header = block.call(raw)
61
+ new_header << "" while new_header.length < (raw.first.try(:length) || 0)
62
+ raw.unshift new_header
63
+ raw
64
+ end
28
65
  end
29
66
 
30
67
  private
@@ -34,17 +71,25 @@ module Chop
34
71
  end
35
72
 
36
73
  def node
37
- @node ||= find(@selector)
74
+ @node ||= session.find(selector)
38
75
  end
39
76
 
40
77
  def row_to_text row
41
- row.all("dt,dd").collect do |cell|
42
- text = cell.text
43
- if text.blank? and image = cell.all("img").first
44
- text = image["alt"]
45
- end
46
- text
78
+ cells(row).map do |cell|
79
+ cell_to_text(cell)
80
+ end
81
+ end
82
+
83
+ def cells row
84
+ row.all("dt,dd")
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"]
47
91
  end
92
+ text
48
93
  end
49
94
  end
50
95
 
@@ -0,0 +1,30 @@
1
+ module Chop
2
+ module DSL
3
+ def create! table, klass, &block
4
+ Builder.build! table, klass, &block
5
+ end
6
+
7
+ def diff! selector, table, session: Capybara.current_session, &block
8
+ class_name = session.find(selector).tag_name.capitalize
9
+ klass = const_get("Chop::#{class_name}")
10
+ klass.diff! selector, table, session: session, &block
11
+ end
12
+ end
13
+ end
14
+
15
+ if defined?(Cucumber::MultilineArgument::DataTable)
16
+ Cucumber::MultilineArgument::DataTable.prepend Module.new {
17
+ def create! klass, &block
18
+ Chop.create! self, klass, &block
19
+ end
20
+
21
+ def diff! other_table, options={}, &block
22
+ if other_table.is_a?(String) && !other_table.include?("|")
23
+ Chop.diff! other_table, self, &block
24
+ else
25
+ super
26
+ end
27
+ end
28
+ }
29
+ end
30
+
@@ -1,33 +1,18 @@
1
- require "delegate"
2
-
3
- module Chop
4
- class Table < SimpleDelegator
5
- def self.diff! table, &block
6
- klass = Class.new(self) do
7
- class_attribute :cell_transformers
8
- self.cell_transformers = []
9
-
10
- def self.cell index, &block
11
- cell_transformers[index] = block
12
- end
13
-
14
- def cell_to_text cell, index
15
- if transformer = cell_transformers[index]
16
- transformer.call cell
17
- else
18
- super
19
- end
20
- end
21
-
22
- instance_eval &block if block_given?
23
- end
1
+ require "active_support/core_ext/object/blank"
24
2
 
25
- klass.new.diff! table
3
+ module Chop
4
+ class Table < Struct.new(:selector, :table, :session, :block)
5
+ def self.diff! selector, table, session: Capybara.current_session, &block
6
+ new(selector, table, session, block).diff!
26
7
  end
27
8
 
28
- def initialize selector = "table", session: Capybara.current_session
29
- super(session)
30
- @selector = selector
9
+ attr_accessor :transformations
10
+
11
+ def initialize(selector = "table", table = nil, session = Capybara.current_session, block = nil, &other_block)
12
+ super
13
+ self.transformations = []
14
+ instance_eval &block if block.respond_to?(:call)
15
+ instance_eval &other_block if block_given?
31
16
  end
32
17
 
33
18
  def header_elements
@@ -35,7 +20,7 @@ module Chop
35
20
  end
36
21
 
37
22
  def header
38
- header_elements.collect do |row|
23
+ header_elements.map do |row|
39
24
  row.all(:xpath, "./*").map(&:text)
40
25
  end
41
26
  end
@@ -45,23 +30,45 @@ module Chop
45
30
  end
46
31
 
47
32
  def body
48
- body_elements.collect do |row|
33
+ body_elements.map do |row|
49
34
  row_to_text(row)
50
35
  end
51
36
  end
52
37
 
53
- def to_a
38
+ def base_to_a
54
39
  header + body
55
40
  end
56
41
 
57
42
  def normalized_to_a
58
- raw = to_a
43
+ raw = base_to_a
59
44
  max = raw.map(&:count).max
60
45
  raw.select { |row| row.count == max }
61
46
  end
62
47
 
63
- def diff! table
64
- table.diff! normalized_to_a
48
+ def to_a
49
+ results = normalized_to_a
50
+ transformations.each { |transformation| transformation.call(results) }
51
+ results
52
+ end
53
+
54
+ def transformation &block
55
+ transformations << block
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
+ def allow_not_found
71
+ @allow_not_found = true
65
72
  end
66
73
 
67
74
  private
@@ -71,7 +78,12 @@ module Chop
71
78
  end
72
79
 
73
80
  def node
74
- @node ||= find(@selector)
81
+ @node ||= begin
82
+ session.find(selector)
83
+ rescue Capybara::ElementNotFound
84
+ raise unless @allow_not_found
85
+ Capybara::Node::Simple.new("")
86
+ end
75
87
  end
76
88
 
77
89
  def row_to_text row
@@ -1,30 +1,44 @@
1
- require "delegate"
1
+ require "active_support/core_ext/object/blank"
2
2
 
3
3
  module Chop
4
- class UnorderedList < SimpleDelegator
5
- def self.diff! table, &block
6
- new.diff! table
4
+ class UnorderedList < 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
7
  end
8
8
 
9
- def initialize selector = "ul", session: Capybara.current_session
10
- super(session)
11
- @selector = selector
9
+ attr_accessor :transformations
10
+
11
+ def initialize selector = "ul", table = nil, session = Capybara.current_session, block = nil, &other_block
12
+ super
13
+ self.transformations = []
14
+ instance_eval &block if block.respond_to?(:call)
15
+ instance_eval &other_block if block_given?
12
16
  end
13
17
 
14
- def to_a
18
+ def base_to_a
15
19
  rows.collect do |row|
16
20
  row_to_text(row)
17
21
  end
18
22
  end
19
23
 
20
24
  def normalized_to_a
21
- raw = to_a
25
+ raw = base_to_a
22
26
  max = raw.map(&:count).max
23
27
  raw.select { |row| row.count == max }
24
28
  end
25
29
 
26
- def diff! table
27
- table.diff! normalized_to_a
30
+ def to_a
31
+ results = normalized_to_a
32
+ transformations.each { |transformation| transformation.call(results) }
33
+ results
34
+ end
35
+
36
+ def transformation &block
37
+ transformations << block
38
+ end
39
+
40
+ def diff! cucumber_table = table
41
+ cucumber_table.diff! to_a
28
42
  end
29
43
 
30
44
  private
@@ -34,7 +48,7 @@ module Chop
34
48
  end
35
49
 
36
50
  def node
37
- @node ||= find(@selector)
51
+ @node ||= session.find(selector)
38
52
  end
39
53
 
40
54
  def row_to_text row
@@ -1,3 +1,3 @@
1
1
  module Chop
2
- VERSION = "0.8.0"
2
+ VERSION = "0.9.0"
3
3
  end
metadata CHANGED
@@ -1,15 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.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-02-18 00:00:00.000000000 Z
11
+ date: 2017-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: cucumber
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: capybara
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
13
55
  - !ruby/object:Gem::Dependency
14
56
  name: bundler
15
57
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +94,20 @@ dependencies:
52
94
  - - ">="
53
95
  - !ruby/object:Gem::Version
54
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: slim
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
55
111
  description: Slice and dice your cucumber tables with ease!
56
112
  email:
57
113
  - micah@botandrose.com
@@ -72,6 +128,7 @@ files:
72
128
  - lib/chop.rb
73
129
  - lib/chop/builder.rb
74
130
  - lib/chop/definition_list.rb
131
+ - lib/chop/dsl.rb
75
132
  - lib/chop/table.rb
76
133
  - lib/chop/unordered_list.rb
77
134
  - lib/chop/version.rb