cucumber_factory 2.0.2 → 2.1.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/CHANGELOG.md +13 -0
- data/Gemfile.cucumber-1.3 +3 -0
- data/Gemfile.cucumber-1.3.lock +4 -1
- data/Gemfile.cucumber-2.4 +3 -0
- data/Gemfile.cucumber-2.4.lock +4 -1
- data/Gemfile.cucumber-3.0 +3 -0
- data/Gemfile.cucumber-3.0.lock +4 -1
- data/Gemfile.cucumber-3.1 +3 -0
- data/Gemfile.cucumber-3.1.lock +4 -1
- data/README.md +1 -1
- data/lib/cucumber_factory/build_strategy.rb +94 -48
- data/lib/cucumber_factory/factory.rb +22 -9
- data/lib/cucumber_factory/version.rb +1 -1
- data/spec/cucumber_factory/factory/build_strategy_spec.rb +12 -14
- data/spec/cucumber_factory/steps_spec.rb +417 -405
- data/spec/spec_helper.rb +3 -0
- data/spec/support/database.rb +6 -0
- data/spec/support/factories/factories.rb +32 -0
- data/spec/support/models/job_offer.rb +1 -3
- data/spec/support/models/opera.rb +2 -3
- metadata +4 -4
- data/spec/support/factory_bot_mock.rb +0 -17
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3153238ae9f4c30fd21e6dd493621e361d4bc6b508521effc93fbb4cb4be8af2
         | 
| 4 | 
            +
              data.tar.gz: c152087fd0231027a94abf15f2abff5c3b3857c714212f3c1eefa67902b32b3e
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 48b39f6ebaedb5bca521d07579384cbcffea01eb4638787de026ce683b1db2abc70fd91227f272cd88a75cb3a15cd8e780989b99a3429313aa6f9e0f6a8575c9
         | 
| 7 | 
            +
              data.tar.gz: 5b16cb2d4e73c1fde061b12e31a23944a3d6889316002805cd0f0bb7d0355cf520d01cb587c5a0ab58648633f83189ee2bba9597b2bda7217315db5fc48ca218
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -13,6 +13,19 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html | |
| 13 13 |  | 
| 14 14 | 
             
            ### Compatible changes
         | 
| 15 15 |  | 
| 16 | 
            +
            -
         | 
| 17 | 
            +
             | 
| 18 | 
            +
             | 
| 19 | 
            +
            ## 2.1.0 - 2020-03-09
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ### Compatible changes
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            - Allow associations to be set for [transient attributes](https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#transient-attributes) if they are named after the model. For example, when there is a `Role` model and the`user` factory has a transient attribute `role`, the following steps are now valid:
         | 
| 24 | 
            +
              ```
         | 
| 25 | 
            +
              Given there is a role
         | 
| 26 | 
            +
                And there is a user with the role above
         | 
| 27 | 
            +
              ```
         | 
| 28 | 
            +
             | 
| 16 29 | 
             
            ## 2.0.2 - 2020-03-26
         | 
| 17 30 |  | 
| 18 31 | 
             
            ### Compatible changes
         | 
    
        data/Gemfile.cucumber-1.3
    CHANGED
    
    
    
        data/Gemfile.cucumber-1.3.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                cucumber_factory (2.0 | 
| 4 | 
            +
                cucumber_factory (2.1.0)
         | 
| 5 5 | 
             
                  activerecord
         | 
| 6 6 | 
             
                  activesupport
         | 
| 7 7 | 
             
                  cucumber
         | 
| @@ -35,6 +35,8 @@ GEM | |
| 35 35 | 
             
                  cucumber
         | 
| 36 36 | 
             
                database_cleaner (1.0.1)
         | 
| 37 37 | 
             
                diff-lcs (1.3)
         | 
| 38 | 
            +
                factory_bot (4.11.1)
         | 
| 39 | 
            +
                  activesupport (>= 3.0.0)
         | 
| 38 40 | 
             
                gemika (0.4.0)
         | 
| 39 41 | 
             
                gherkin (2.12.2)
         | 
| 40 42 | 
             
                  multi_json (~> 1.3)
         | 
| @@ -71,6 +73,7 @@ DEPENDENCIES | |
| 71 73 | 
             
              cucumber (~> 1.3.20)
         | 
| 72 74 | 
             
              cucumber_factory!
         | 
| 73 75 | 
             
              database_cleaner (~> 1.0.0)
         | 
| 76 | 
            +
              factory_bot (< 5)
         | 
| 74 77 | 
             
              gemika
         | 
| 75 78 | 
             
              mysql2
         | 
| 76 79 | 
             
              rake (>= 10.0.4)
         | 
    
        data/Gemfile.cucumber-2.4
    CHANGED
    
    
    
        data/Gemfile.cucumber-2.4.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                cucumber_factory (2.0 | 
| 4 | 
            +
                cucumber_factory (2.1.0)
         | 
| 5 5 | 
             
                  activerecord
         | 
| 6 6 | 
             
                  activesupport
         | 
| 7 7 | 
             
                  cucumber
         | 
| @@ -40,6 +40,8 @@ GEM | |
| 40 40 | 
             
                  cucumber
         | 
| 41 41 | 
             
                database_cleaner (1.6.2)
         | 
| 42 42 | 
             
                diff-lcs (1.3)
         | 
| 43 | 
            +
                factory_bot (4.11.1)
         | 
| 44 | 
            +
                  activesupport (>= 3.0.0)
         | 
| 43 45 | 
             
                gemika (0.3.2)
         | 
| 44 46 | 
             
                gherkin (4.1.3)
         | 
| 45 47 | 
             
                i18n (0.9.5)
         | 
| @@ -75,6 +77,7 @@ DEPENDENCIES | |
| 75 77 | 
             
              cucumber (~> 2.4.0)
         | 
| 76 78 | 
             
              cucumber_factory!
         | 
| 77 79 | 
             
              database_cleaner
         | 
| 80 | 
            +
              factory_bot (< 5)
         | 
| 78 81 | 
             
              gemika
         | 
| 79 82 | 
             
              mysql2
         | 
| 80 83 | 
             
              rake
         | 
    
        data/Gemfile.cucumber-3.0
    CHANGED
    
    
    
        data/Gemfile.cucumber-3.0.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                cucumber_factory (2.0 | 
| 4 | 
            +
                cucumber_factory (2.1.0)
         | 
| 5 5 | 
             
                  activerecord
         | 
| 6 6 | 
             
                  activesupport
         | 
| 7 7 | 
             
                  cucumber
         | 
| @@ -45,6 +45,8 @@ GEM | |
| 45 45 | 
             
                  cucumber
         | 
| 46 46 | 
             
                database_cleaner (1.6.2)
         | 
| 47 47 | 
             
                diff-lcs (1.3)
         | 
| 48 | 
            +
                factory_bot (5.1.1)
         | 
| 49 | 
            +
                  activesupport (>= 4.2.0)
         | 
| 48 50 | 
             
                gemika (0.3.2)
         | 
| 49 51 | 
             
                gherkin (4.1.3)
         | 
| 50 52 | 
             
                i18n (0.9.5)
         | 
| @@ -80,6 +82,7 @@ DEPENDENCIES | |
| 80 82 | 
             
              cucumber (~> 3.0.0)
         | 
| 81 83 | 
             
              cucumber_factory!
         | 
| 82 84 | 
             
              database_cleaner
         | 
| 85 | 
            +
              factory_bot
         | 
| 83 86 | 
             
              gemika
         | 
| 84 87 | 
             
              mysql2
         | 
| 85 88 | 
             
              rake
         | 
    
        data/Gemfile.cucumber-3.1
    CHANGED
    
    
    
        data/Gemfile.cucumber-3.1.lock
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            PATH
         | 
| 2 2 | 
             
              remote: .
         | 
| 3 3 | 
             
              specs:
         | 
| 4 | 
            -
                cucumber_factory (2.0 | 
| 4 | 
            +
                cucumber_factory (2.1.0)
         | 
| 5 5 | 
             
                  activerecord
         | 
| 6 6 | 
             
                  activesupport
         | 
| 7 7 | 
             
                  cucumber
         | 
| @@ -45,6 +45,8 @@ GEM | |
| 45 45 | 
             
                  cucumber
         | 
| 46 46 | 
             
                database_cleaner (1.6.2)
         | 
| 47 47 | 
             
                diff-lcs (1.3)
         | 
| 48 | 
            +
                factory_bot (5.1.1)
         | 
| 49 | 
            +
                  activesupport (>= 4.2.0)
         | 
| 48 50 | 
             
                gemika (0.3.2)
         | 
| 49 51 | 
             
                gherkin (5.0.0)
         | 
| 50 52 | 
             
                i18n (0.9.5)
         | 
| @@ -80,6 +82,7 @@ DEPENDENCIES | |
| 80 82 | 
             
              cucumber (~> 3.1.0)
         | 
| 81 83 | 
             
              cucumber_factory!
         | 
| 82 84 | 
             
              database_cleaner
         | 
| 85 | 
            +
              factory_bot
         | 
| 83 86 | 
             
              gemika
         | 
| 84 87 | 
             
              mysql2
         | 
| 85 88 | 
             
              rake
         | 
    
        data/README.md
    CHANGED
    
    | @@ -65,7 +65,7 @@ Given there is a movie with these attributes: | |
| 65 65 | 
             
            Setting associations
         | 
| 66 66 | 
             
            --------------------
         | 
| 67 67 |  | 
| 68 | 
            -
            You can set `belongs_to` associations by referring to the last created record of as `above`:
         | 
| 68 | 
            +
            You can set `belongs_to` and `transient` associations by referring to the last created record of as `above`:
         | 
| 69 69 |  | 
| 70 70 | 
             
            ```cucumber
         | 
| 71 71 | 
             
            Given there is a movie with the title "Before Sunrise"
         | 
| @@ -7,79 +7,110 @@ module CucumberFactory | |
| 7 7 | 
             
                class << self
         | 
| 8 8 |  | 
| 9 9 | 
             
                  def from_prose(model_prose, variant_prose)
         | 
| 10 | 
            -
                     | 
| 11 | 
            -
                     | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
                       | 
| 10 | 
            +
                    variants = variants_from_prose(variant_prose)
         | 
| 11 | 
            +
                    factory = factory_bot_factory(model_prose, variants)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    if factory
         | 
| 14 | 
            +
                      strategy = factory_bot_strategy(factory, model_prose, variants)
         | 
| 15 | 
            +
                      transient_attributes = factory_bot_transient_attributes(factory, variants)
         | 
| 15 16 | 
             
                    else
         | 
| 16 | 
            -
                       | 
| 17 | 
            +
                      strategy = alternative_strategy(model_prose, variants)
         | 
| 18 | 
            +
                      transient_attributes = []
         | 
| 17 19 | 
             
                    end
         | 
| 18 20 |  | 
| 19 | 
            -
                     | 
| 20 | 
            -
             | 
| 21 | 
            +
                    [strategy, transient_attributes]
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                  private
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  def variants_from_prose(variant_prose)
         | 
| 27 | 
            +
                    if variant_prose.present?
         | 
| 28 | 
            +
                      variants = /\((.*?)\)/.match(variant_prose)[1].split(/\s*,\s*/)
         | 
| 29 | 
            +
                      variants.collect { |variant| variant.downcase.gsub(" ", "_").to_sym }
         | 
| 21 30 | 
             
                    else
         | 
| 22 | 
            -
                       | 
| 23 | 
            -
                      machinist_strategy(model_class, variants) ||
         | 
| 24 | 
            -
                        active_record_strategy(model_class) ||
         | 
| 25 | 
            -
                        ruby_object_strategy(model_class)
         | 
| 31 | 
            +
                      []
         | 
| 26 32 | 
             
                    end
         | 
| 27 33 | 
             
                  end
         | 
| 28 34 |  | 
| 29 | 
            -
                   | 
| 35 | 
            +
                  def factory_bot_factory(model_prose, variants)
         | 
| 36 | 
            +
                    return unless factory_bot_class
         | 
| 30 37 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
                     | 
| 33 | 
            -
                    factory_class ||= ::FactoryGirl if defined?(FactoryGirl)
         | 
| 34 | 
            -
                    return unless factory_class
         | 
| 38 | 
            +
                    factory_name = factory_name_from_prose(model_prose)
         | 
| 39 | 
            +
                    factory = factory_bot_class.factories[factory_name]
         | 
| 35 40 |  | 
| 36 | 
            -
                     | 
| 37 | 
            -
             | 
| 41 | 
            +
                    if factory.nil? && variants.present?
         | 
| 42 | 
            +
                      factory = factory_bot_class.factories[variants[0]]
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    factory
         | 
| 46 | 
            +
                  end
         | 
| 38 47 |  | 
| 39 | 
            -
             | 
| 48 | 
            +
                  def factory_bot_strategy(factory, model_prose, variants)
         | 
| 49 | 
            +
                    return unless factory
         | 
| 40 50 |  | 
| 41 | 
            -
                     | 
| 51 | 
            +
                    factory_name = factory_name_from_prose(model_prose)
         | 
| 52 | 
            +
                    if factory_bot_class.factories[factory_name].nil? && variants.present?
         | 
| 42 53 | 
             
                      factory_name, *variants = variants
         | 
| 43 54 | 
             
                    end
         | 
| 44 55 |  | 
| 45 | 
            -
                     | 
| 46 | 
            -
                       | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 56 | 
            +
                    new(factory.build_class) do |attributes|
         | 
| 57 | 
            +
                      # Cannot have additional scalar args after a varargs
         | 
| 58 | 
            +
                      # argument in Ruby 1.8 and 1.9
         | 
| 59 | 
            +
                      args = []
         | 
| 60 | 
            +
                      args += variants
         | 
| 61 | 
            +
                      args << attributes
         | 
| 62 | 
            +
                      factory_bot_class.create(factory_name, *args)
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  def factory_bot_transient_attributes(factory, variants)
         | 
| 67 | 
            +
                    return [] unless factory
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    factory_attributes = factory_bot_attributes(factory, variants)
         | 
| 70 | 
            +
                    class_attributes = factory.build_class.attribute_names.map(&:to_sym)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    factory_attributes - class_attributes
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def factory_bot_attributes(factory, variants)
         | 
| 76 | 
            +
                    traits = factory_bot_traits(factory, variants)
         | 
| 77 | 
            +
                    factory.with_traits(traits.map(&:name)).definition.attributes.names
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  def factory_bot_traits(factory, variants)
         | 
| 81 | 
            +
                    factory.definition.defined_traits.select do |trait|
         | 
| 82 | 
            +
                      variants.include?(trait.name.to_sym)
         | 
| 54 83 | 
             
                    end
         | 
| 84 | 
            +
                  end
         | 
| 55 85 |  | 
| 86 | 
            +
                  def alternative_strategy(model_prose, variants)
         | 
| 87 | 
            +
                    model_class = underscored_model_name(model_prose).camelize.constantize
         | 
| 88 | 
            +
                    machinist_strategy(model_class, variants) ||
         | 
| 89 | 
            +
                      active_record_strategy(model_class) ||
         | 
| 90 | 
            +
                      ruby_object_strategy(model_class)
         | 
| 56 91 | 
             
                  end
         | 
| 57 92 |  | 
| 58 93 | 
             
                  def machinist_strategy(model_class, variants)
         | 
| 59 | 
            -
                     | 
| 60 | 
            -
             | 
| 61 | 
            -
                      new(model_class) do |attributes|
         | 
| 62 | 
            -
                        if variants.present?
         | 
| 63 | 
            -
                          variants.size == 1 or raise 'Machinist only supports a single variant per blueprint'
         | 
| 64 | 
            -
                          model_class.make(variants.first.to_sym, attributes)
         | 
| 65 | 
            -
                        else
         | 
| 66 | 
            -
                          model_class.make(attributes)
         | 
| 67 | 
            -
                        end
         | 
| 68 | 
            -
                      end
         | 
| 94 | 
            +
                    return unless model_class.respond_to?(:make)
         | 
| 69 95 |  | 
| 96 | 
            +
                    new(model_class) do |attributes|
         | 
| 97 | 
            +
                      if variants.present?
         | 
| 98 | 
            +
                        variants.size == 1 or raise 'Machinist only supports a single variant per blueprint'
         | 
| 99 | 
            +
                        model_class.make(variants.first, attributes)
         | 
| 100 | 
            +
                      else
         | 
| 101 | 
            +
                        model_class.make(attributes)
         | 
| 102 | 
            +
                      end
         | 
| 70 103 | 
             
                    end
         | 
| 71 104 | 
             
                  end
         | 
| 72 105 |  | 
| 73 106 | 
             
                  def active_record_strategy(model_class)
         | 
| 74 | 
            -
                     | 
| 75 | 
            -
             | 
| 76 | 
            -
                      new(model_class) do |attributes|
         | 
| 77 | 
            -
                        model = model_class.new
         | 
| 78 | 
            -
                        CucumberFactory::Switcher.assign_attributes(model, attributes)
         | 
| 79 | 
            -
                        model.save!
         | 
| 80 | 
            -
                        model
         | 
| 81 | 
            -
                      end
         | 
| 107 | 
            +
                    return unless model_class.respond_to?(:create!)
         | 
| 82 108 |  | 
| 109 | 
            +
                    new(model_class) do |attributes|
         | 
| 110 | 
            +
                      model = model_class.new
         | 
| 111 | 
            +
                      CucumberFactory::Switcher.assign_attributes(model, attributes)
         | 
| 112 | 
            +
                      model.save!
         | 
| 113 | 
            +
                      model
         | 
| 83 114 | 
             
                    end
         | 
| 84 115 | 
             
                  end
         | 
| 85 116 |  | 
| @@ -89,6 +120,21 @@ module CucumberFactory | |
| 89 120 | 
             
                    end
         | 
| 90 121 | 
             
                  end
         | 
| 91 122 |  | 
| 123 | 
            +
                  def factory_bot_class
         | 
| 124 | 
            +
                    factory_class = ::FactoryBot if defined?(FactoryBot)
         | 
| 125 | 
            +
                    factory_class ||= ::FactoryGirl if defined?(FactoryGirl)
         | 
| 126 | 
            +
                    factory_class
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                  def factory_name_from_prose(model_prose)
         | 
| 130 | 
            +
                    underscored_model_name(model_prose).to_s.underscore.gsub('/', '_').to_sym
         | 
| 131 | 
            +
                  end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                  def underscored_model_name(model_prose)
         | 
| 134 | 
            +
                    # don't use \w which depends on the system locale
         | 
| 135 | 
            +
                    model_prose.gsub(/[^A-Za-z0-9_\/]+/, "_")
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
             | 
| 92 138 | 
             
                end
         | 
| 93 139 |  | 
| 94 140 |  | 
| @@ -100,7 +100,7 @@ module CucumberFactory | |
| 100 100 | 
             
                  end
         | 
| 101 101 |  | 
| 102 102 | 
             
                  def parse_creation(world, raw_model, raw_variant, raw_attributes, raw_boolean_attributes, raw_multiline_attributes = nil)
         | 
| 103 | 
            -
                    build_strategy = BuildStrategy.from_prose(raw_model, raw_variant)
         | 
| 103 | 
            +
                    build_strategy, transient_attributes = BuildStrategy.from_prose(raw_model, raw_variant)
         | 
| 104 104 | 
             
                    model_class = build_strategy.model_class
         | 
| 105 105 | 
             
                    attributes = {}
         | 
| 106 106 | 
             
                    if raw_attributes.try(:strip).present?
         | 
| @@ -108,7 +108,7 @@ module CucumberFactory | |
| 108 108 | 
             
                      raw_attributes.scan(raw_attribute_fragment_regex).each do |fragment|
         | 
| 109 109 | 
             
                        attribute = attribute_name_from_prose(fragment[0])
         | 
| 110 110 | 
             
                        value = fragment[1]
         | 
| 111 | 
            -
                        attributes[attribute] = attribute_value(world, model_class, attribute, value)
         | 
| 111 | 
            +
                        attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
         | 
| 112 112 | 
             
                      end
         | 
| 113 113 | 
             
                      unused_raw_attributes = raw_attributes.gsub(raw_attribute_fragment_regex, '')
         | 
| 114 114 | 
             
                      if unused_raw_attributes.present?
         | 
| @@ -129,14 +129,14 @@ module CucumberFactory | |
| 129 129 | 
             
                          raw_attribute, value = fragment.split(': ')
         | 
| 130 130 | 
             
                          attribute = attribute_name_from_prose(raw_attribute)
         | 
| 131 131 | 
             
                          value = "\"#{value}\"" unless matches_fully?(value, VALUE_ARRAY)
         | 
| 132 | 
            -
                          attributes[attribute] = attribute_value(world, model_class, attribute, value)
         | 
| 132 | 
            +
                          attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
         | 
| 133 133 | 
             
                        end
         | 
| 134 134 | 
             
                      # DataTable e.g. in raw [["first name", "Jane"], ["last name", "Jenny"]]
         | 
| 135 135 | 
             
                      else
         | 
| 136 136 | 
             
                        raw_multiline_attributes.raw.each do |raw_attribute, value|
         | 
| 137 137 | 
             
                          attribute = attribute_name_from_prose(raw_attribute)
         | 
| 138 138 | 
             
                          value = "\"#{value}\"" unless matches_fully?(value, VALUE_ARRAY)
         | 
| 139 | 
            -
                          attributes[attribute] = attribute_value(world, model_class, attribute, value)
         | 
| 139 | 
            +
                          attributes[attribute] = attribute_value(world, model_class, transient_attributes, attribute, value)
         | 
| 140 140 | 
             
                        end
         | 
| 141 141 | 
             
                      end
         | 
| 142 142 | 
             
                    end
         | 
| @@ -145,15 +145,15 @@ module CucumberFactory | |
| 145 145 | 
             
                    record
         | 
| 146 146 | 
             
                  end
         | 
| 147 147 |  | 
| 148 | 
            -
                  def attribute_value(world, model_class, attribute, value)
         | 
| 149 | 
            -
                     | 
| 148 | 
            +
                  def attribute_value(world, model_class, transient_attributes, attribute, value)
         | 
| 149 | 
            +
                    association_class = resolve_association_class(attribute, model_class, transient_attributes)
         | 
| 150 150 |  | 
| 151 151 | 
             
                    if matches_fully?(value, VALUE_ARRAY)
         | 
| 152 152 | 
             
                      elements_str = unquote(value)
         | 
| 153 | 
            -
                      value = elements_str.scan(VALUE_SCALAR).map { |v| attribute_value(world, model_class, attribute, v) }
         | 
| 154 | 
            -
                    elsif  | 
| 153 | 
            +
                      value = elements_str.scan(VALUE_SCALAR).map { |v| attribute_value(world, model_class, transient_attributes, attribute, v) }
         | 
| 154 | 
            +
                    elsif association_class.present?
         | 
| 155 155 | 
             
                      if matches_fully?(value, VALUE_LAST_RECORD)
         | 
| 156 | 
            -
                        value = CucumberFactory::Switcher.find_last( | 
| 156 | 
            +
                        value = CucumberFactory::Switcher.find_last(association_class) or raise Error, "There is no last #{attribute}"
         | 
| 157 157 | 
             
                      elsif matches_fully?(value, VALUE_STRING)
         | 
| 158 158 | 
             
                        value = unquote(value)
         | 
| 159 159 | 
             
                        value = get_named_record(world, value) || transform_value(world, value)
         | 
| @@ -169,6 +169,19 @@ module CucumberFactory | |
| 169 169 | 
             
                    value
         | 
| 170 170 | 
             
                  end
         | 
| 171 171 |  | 
| 172 | 
            +
                  def resolve_association_class(attribute, model_class, transient_attributes)
         | 
| 173 | 
            +
                    return unless model_class.respond_to?(:reflect_on_association)
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                    klass = if model_class.reflect_on_association(attribute)
         | 
| 176 | 
            +
                      model_class.reflect_on_association(attribute).klass
         | 
| 177 | 
            +
                    elsif transient_attributes.include?(attribute.to_sym)
         | 
| 178 | 
            +
                      klass_name = attribute.to_s.camelize
         | 
| 179 | 
            +
                      klass_name.constantize if Object.const_defined?(klass_name)
         | 
| 180 | 
            +
                    else
         | 
| 181 | 
            +
                      nil
         | 
| 182 | 
            +
                    end
         | 
| 183 | 
            +
                  end
         | 
| 184 | 
            +
             | 
| 172 185 | 
             
                  def resolve_scalar_value(world, model_class, attribute, value)
         | 
| 173 186 | 
             
                    if matches_fully?(value, VALUE_STRING)
         | 
| 174 187 | 
             
                      value = unquote(value)
         | 
| @@ -2,47 +2,45 @@ require 'spec_helper' | |
| 2 2 |  | 
| 3 3 | 
             
            describe CucumberFactory::BuildStrategy do
         | 
| 4 4 |  | 
| 5 | 
            -
              subject { CucumberFactory::BuildStrategy }
         | 
| 6 | 
            -
             | 
| 7 5 | 
             
              # most of the behaviour is integration tested in steps_spec.rb
         | 
| 8 6 |  | 
| 9 7 | 
             
              describe '.from_prose' do
         | 
| 10 8 |  | 
| 11 9 | 
             
                context 'when describing a factory_bot factory' do
         | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
                    FactoryBot.stub_factories :job_offer => JobOffer
         | 
| 15 | 
            -
                    strategy = subject.from_prose('job offer', nil)
         | 
| 10 | 
            +
                  it 'returns a strategy and transient attributes corresponding to the factories model' do
         | 
| 11 | 
            +
                    strategy, transient_attributes = described_class.from_prose('job offer', nil)
         | 
| 16 12 |  | 
| 17 13 | 
             
                    strategy.should be_a(described_class)
         | 
| 18 14 | 
             
                    strategy.model_class.should == JobOffer
         | 
| 15 | 
            +
                    transient_attributes.should == [:my_transient_attribute]
         | 
| 19 16 | 
             
                  end
         | 
| 20 17 |  | 
| 21 18 | 
             
                  it 'uses the variant for the factory name if present' do
         | 
| 22 | 
            -
                     | 
| 23 | 
            -
                    strategy = subject.from_prose('foo', '(job offer)')
         | 
| 19 | 
            +
                    strategy, transient_attributes = described_class.from_prose('job offer', '(tempting_job_offer)')
         | 
| 24 20 |  | 
| 25 21 | 
             
                    strategy.should be_a(described_class)
         | 
| 26 22 | 
             
                    strategy.model_class.should == JobOffer
         | 
| 23 | 
            +
                    transient_attributes.should == [:my_transient_attribute, :other_transient_attribute]
         | 
| 27 24 | 
             
                  end
         | 
| 28 | 
            -
             | 
| 29 25 | 
             
                end
         | 
| 30 26 |  | 
| 31 27 | 
             
                context 'when describing a non factory_bot model' do
         | 
| 28 | 
            +
                  before do
         | 
| 29 | 
            +
                    hide_const("FactoryBot")
         | 
| 30 | 
            +
                  end
         | 
| 32 31 |  | 
| 33 32 | 
             
                  it "should return a strategy for the class matching a natural language expression" do
         | 
| 34 | 
            -
                     | 
| 35 | 
            -
                     | 
| 33 | 
            +
                    described_class.from_prose("movie", nil).first.model_class.should == Movie
         | 
| 34 | 
            +
                    described_class.from_prose("job offer", nil).first.model_class.should == JobOffer
         | 
| 36 35 | 
             
                  end
         | 
| 37 36 |  | 
| 38 37 | 
             
                  it "should ignore variants for the class name" do
         | 
| 39 | 
            -
                     | 
| 38 | 
            +
                    described_class.from_prose("movie", "(job offer)").first.model_class.should == Movie
         | 
| 40 39 | 
             
                  end
         | 
| 41 40 |  | 
| 42 41 | 
             
                  it "should allow namespaced models" do
         | 
| 43 | 
            -
                     | 
| 42 | 
            +
                    described_class.from_prose("people/actor", nil).first.model_class.should == People::Actor
         | 
| 44 43 | 
             
                  end
         | 
| 45 | 
            -
             | 
| 46 44 | 
             
                end
         | 
| 47 45 |  | 
| 48 46 | 
             
              end
         |