chop 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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