usine 1.1.0 → 1.2.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: 989d0ef56df43856ceb3ceee137fdaf56c5d722a
4
- data.tar.gz: 79dc66c1c26e026fb6bbfa87955a394582042701
3
+ metadata.gz: 03d1e6d91e2856f9104d7a7c7b6907988674cdf8
4
+ data.tar.gz: 7573d835285b97a7243304ab437bd43ae425e150
5
5
  SHA512:
6
- metadata.gz: 2b3cc1296fc2abf5c986101c85742ba18baafc109a55f0700d5d9c6c9361e312701cc2c0488935f8abafd46b1cba1f00cdc04d495e2273e704133ce4ea9dd0cd
7
- data.tar.gz: c12b2dfda625558ead73b5cfc1581ae9791ca210b549e569769b4ed93e56c6b515793502f2b837b0e8f281fe7d432e0d29413f10a17d099ec88886d300a82cbb
6
+ metadata.gz: fcc0c03dff27f57b4a1d63eda0c11803ee7bb8fee770b8eb9fa2378fe0851b65fa6eddee7003741d89a8a15351b49585fe1ba013603e9332eb26387fc4537028
7
+ data.tar.gz: df77515eb45fe2bec61f553fa20c7e696beb544ca93a753895bbf28f4772bd17fec5fa7ae59219d03d30e710648017ca0480eef1343dc3288705e2a73f21436e
data/.gitignore CHANGED
@@ -7,3 +7,5 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+
11
+ *.gem
data/README.md CHANGED
@@ -1,9 +1,6 @@
1
1
  # Usine
2
2
 
3
- Usine (french word for factory) is a small gem aiming to bring a limited feature set of factory_girl
4
- when using Trailblazer’s operations. [Trailblazer](http://trailblazer.to/) advocates against using factory_girl to avoid
5
- differences when initializing objects. Usine follows this path but allows you to define defaults for the
6
- parameters passed to your operations.
3
+ Usine (french word for factory) is a wrapper around factory_girls with two goals: simplify using [Trailblazer](http://trailblazer.to/) with factory_girl, and avoid initializing models through factory_girl instead of Trailblazer.
7
4
 
8
5
  ## Installation
9
6
 
@@ -23,72 +20,96 @@ Or install it yourself as:
23
20
 
24
21
  ## Usage
25
22
 
26
- ### Definitions
27
-
28
- Definitions are the base of anything in Usine, they are used in factories to generate
29
- the hash of params sent to the operation.
23
+ Usine let you reuse your factories and use them to define the params sent to your Operation.
30
24
 
31
25
  ```ruby
32
- Usine.definition(:item) do
33
- title { "Some title" }
26
+ FactoryGirl.define do
27
+ factory :item do
28
+ title { "Default title"}
29
+ end
30
+
31
+ factory :user do
32
+ email { "xx@exxample.com"}
33
+ end
34
34
  end
35
35
 
36
- Usine.definition(:user) do
37
- email { "j.jaffeux@example.com" }
36
+ # If no symbol given, Usine will try to find an existing factory with this name
37
+ Usine.operation(Item::Create) do
38
+ item
39
+ current_user :user
38
40
  end
39
- ```
40
41
 
41
- ### Factories
42
+ # Usine will transmit [call, run, present] to the operation invocation
43
+ # so you can have the kind of operation you want in your test
44
+ # for example `present` will not run process in your Operation
45
+ Usine.(Item::Create, item: {title: "Another title"})
46
+ Usine.call(Item::Create, item: {title: "Another title"})
47
+ Usine.run(Item::Create, item: {title: "Another title"})
48
+ Usine.present(Item::Create, item: {title: "Another title"})
49
+ ```
42
50
 
43
- Factories let you build a hash which will be sent to an operation when you need it.
44
- This is what you will call when building your objects in a test.
51
+ ### Model less factories
45
52
 
53
+ If your params are not related to a model you can simply create a factory this way:
46
54
  ```ruby
47
- Usine.factory(Item::Create) do
48
- item # if not block given, it will invoke use(:item), which means it will use the :item Definition
49
- current_user use(:user)
55
+ FactoryGirl.define do
56
+ factory :search, class:Usine::NullModel do
57
+ query { "*"}
58
+ end
50
59
  end
51
60
  ```
52
61
 
53
- ### Invoking factories
62
+ ### Factory girl features
54
63
 
55
- Usine respects Trailblazer naming differences when creating an operation : call, run and present.
56
- Which means you have 3 different ways to invoke a factory :
64
+ All factory_girl features should work, sequences for example are working:
57
65
 
58
66
  ```ruby
59
- Usine.(Item::Create, item: {title: "different title"})
60
- Usine.run(Item::Create, item: {title: "different title"})
61
- Usine.present(Item::Create, item: {title: "different title"})
67
+ FactoryGirl.define do
68
+ sequence(:email) do |n|
69
+ "some_email_#{n}@example.com"
70
+ end
71
+
72
+ factory :user do
73
+ contact { generate(:email) }
74
+ end
75
+ end
62
76
  ```
63
77
 
64
- `present` for example can be used to access model/contract without calling process in the operation.
78
+ ### Writing a test with Usine
65
79
 
66
- Let see an example in a test :
67
80
  ```
68
- let(:user_id) {
69
- Usine.(User::Create).model.id
81
+ # Without Usine
82
+ let(:item) {
83
+ Item::Create.({
84
+ item: {
85
+ title: "DEFAULT_TITLE"
86
+ },
87
+ current_user: User::Create.(email: "some_email@example.com").model
88
+ }).model
70
89
  }
71
- ```
72
90
 
73
- ### Sequences
91
+ # With Usine
92
+ FactoryGirl.define do
93
+ factory :item do
94
+ title { "DEFAULT_TITLE" }
95
+ end
74
96
 
75
- Sequences are used in Definitions to help you create different occurences of the same kind of attribute.
76
-
77
- ```ruby
78
- # global sequence
79
- Usine.sequence(:title) do |n|
80
- "title number #{n}"
97
+ factory :user do
98
+ email { "some_email@example.com" }
99
+ end
81
100
  end
82
101
 
83
- # inline sequence in a definition
84
- Usine.definition(:user) do
85
- sequence(:email) { |n| "joffrey_#{n}@example.com" }
86
- email # if no block given, Usine will try to invoke generate(:email)
87
- title # and will also search in global sequences
88
- alt_email { generate(:email) }
102
+ Usine.operation(Item::Create) do
103
+ item
104
+ current_user :user
89
105
  end
106
+
107
+ let(:item) { Usine.(Item::Create).model }
90
108
  ```
91
109
 
110
+ The second example might look more verbose, but you only have to define factories/operations one time.
111
+ And then you can reuse it for all your tests.
112
+
92
113
  ## Contributing
93
114
 
94
115
  Bug reports and pull requests are welcome on GitHub at https://github.com/jjaffeux/usine. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -1,68 +1,27 @@
1
+ require "uber/inheritable_attr"
2
+ require "factory_girl"
1
3
  require "usine/version"
2
4
  require "usine/utils"
3
- require "usine/factory"
4
- require "usine/factory_evaluator"
5
- require "usine/exceptions"
6
- require "usine/definition_evaluator"
7
- require "usine/definition"
8
- require "usine/sequence"
9
- require "uber/inheritable_attr"
5
+ require "usine/null_model"
6
+ require "usine/operation_factory"
7
+ require "usine/operation_wrapper"
10
8
 
11
9
  module Usine
12
10
  extend Uber::InheritableAttr
13
- inheritable_attr :definitions
14
- self.definitions = []
15
- inheritable_attr :factories
16
- self.factories = []
17
- inheritable_attr :sequences
18
- self.sequences = []
11
+ inheritable_attr :operations
12
+ self.operations = []
19
13
 
20
14
  class << self
21
-
22
15
  [:call, :run, :present].each do |mode|
23
16
  define_method mode, ->(*args, &block) {
24
17
  operation = args.shift
25
- factory = find_factory_for_operation(operation)
26
- FactoryEvaluator.call(factory, mode, args.shift)
18
+ attributes = args.shift || {}
19
+ OperationWrapper.(mode, operation, attributes)
27
20
  }
28
21
  end
29
22
 
30
- def factory(operation, *extensions, &block)
31
- self.factories ||= []
32
-
33
- self.factories << Factory.new(operation, *extensions, &block)
34
-
35
- extensions.each do |extension|
36
- self.factories << Factory.new(operation, *extension, &block)
37
- end
38
- end
39
-
40
- def sequence(name, initial_value = 1, &block)
41
- self.sequences << Sequence.new(name, initial_value, &block)
42
- end
43
-
44
- def definition(attribute, *extensions, &block)
45
- self.definitions ||= []
46
-
47
- self.definitions << Definition.new(attribute, &block)
48
-
49
- extensions.each do |extension|
50
- self.definitions << Definition.new(attribute, &block)
51
- end
52
- end
53
-
54
- protected
55
-
56
- def find_factory_for_operation(operation)
57
- factory = Usine.factories.detect do |f|
58
- f.operation == operation
59
- end
60
-
61
- if factory.nil?
62
- raise "NOT FOUND FACTORY for #{operation}"
63
- end
64
-
65
- factory
23
+ def operation(operation, *args, &block)
24
+ self.operations << OperationFactory.new(operation, &block)
66
25
  end
67
26
  end
68
27
  end
@@ -0,0 +1,3 @@
1
+ module Usine
2
+ class NullModel; end
3
+ end
@@ -0,0 +1,31 @@
1
+ module Usine
2
+ class OperationFactory
3
+ attr_reader :operation_class
4
+
5
+ def initialize(operation_class, &block)
6
+ @operation_class = operation_class
7
+ @block = block
8
+ @attributes = {}
9
+ end
10
+
11
+ def process
12
+ self.instance_eval(&@block)
13
+ @attributes
14
+ end
15
+
16
+ def method_missing(method_symbol, factory_name = nil, &block)
17
+ if factory_name
18
+ @attributes[method_symbol] = attributes_for_factory_name(factory_name)
19
+ else
20
+ @attributes[method_symbol] = attributes_for_factory_name(method_symbol)
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ def attributes_for_factory_name(name)
27
+ factory = FactoryGirl.factory_by_name(name)
28
+ factory.run(FactoryGirl::Strategy::AttributesFor, {})
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,49 @@
1
+ module Usine
2
+ class OperationWrapper
3
+ def initialize(mode, operation, attributes = {})
4
+ @mode = mode
5
+ @operation = operation
6
+ @operation_factory = find_operation_factory(operation)
7
+ @attributes = attributes
8
+ end
9
+
10
+ def operations=(operations)
11
+ @operations = operations
12
+ end
13
+
14
+ def operations
15
+ @operations ||= Usine.operations
16
+ end
17
+
18
+ def self.call(mode, operation, attributes = {})
19
+ wrapper = new(mode, operation, attributes)
20
+ wrapper.process
21
+ end
22
+
23
+ def process
24
+ factory_attributes = @operation_factory.process
25
+ merged_attributes = Utils.merge_hashes(factory_attributes, @attributes)
26
+ @operation.send(@mode, merged_attributes)
27
+ end
28
+
29
+ protected
30
+
31
+ def find_operation_factory(operation_class)
32
+ operation_factory = operations.detect do |op|
33
+ op.operation_class == operation_class
34
+ end
35
+
36
+ unless operation_factory
37
+ message = <<-MSG
38
+ Couldn’t find an operation factory for: #{operation_class}
39
+
40
+ Please define it with:
41
+ Usine.operation(#{operation_class}) {}
42
+ MSG
43
+ raise(ArgumentError, message)
44
+ end
45
+
46
+ operation_factory
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Usine
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_dependency "uber"
23
+ spec.add_dependency "factory_girl"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.12"
25
26
  spec.add_development_dependency "rake", "~> 10.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: usine
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joffrey JAFFEUX
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: factory_girl
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'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: bundler
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -98,12 +112,9 @@ files:
98
112
  - bin/console
99
113
  - bin/setup
100
114
  - lib/usine.rb
101
- - lib/usine/definition.rb
102
- - lib/usine/definition_evaluator.rb
103
- - lib/usine/exceptions.rb
104
- - lib/usine/factory.rb
105
- - lib/usine/factory_evaluator.rb
106
- - lib/usine/sequence.rb
115
+ - lib/usine/null_model.rb
116
+ - lib/usine/operation_factory.rb
117
+ - lib/usine/operation_wrapper.rb
107
118
  - lib/usine/utils.rb
108
119
  - lib/usine/version.rb
109
120
  - usine.gemspec
@@ -1,11 +0,0 @@
1
- module Usine
2
- class Definition
3
- attr_reader :name
4
- attr_reader :block
5
-
6
- def initialize(name, *args, &block)
7
- @name = name
8
- @block = block
9
- end
10
- end
11
- end
@@ -1,39 +0,0 @@
1
- module Usine
2
- class DefinitionEvaluator
3
- attr_reader :attributes
4
- attr_reader :sequences
5
- attr_reader :scoped_sequences
6
-
7
- def initialize(sequences = [])
8
- @sequences = sequences
9
- @scoped_sequences = []
10
- @attributes = {}
11
- end
12
-
13
- def method_missing(method_symbol, *args, &block)
14
- if block
15
- @attributes[method_symbol] = block.call
16
- else
17
- @attributes[method_symbol] = generate(method_symbol)
18
- end
19
- end
20
-
21
- def generate(attribute)
22
- if sequence = scoped_sequences.detect { |x| x.attribute == attribute }
23
- sequence.next
24
- elsif sequence = sequences.detect { |x| x.attribute == attribute }
25
- sequence.next
26
- else
27
- message = <<-MSG
28
- Couldn’t find a sequence named: #{attribute}
29
- Available sequences: #{scoped_sequences.concat(sequences).map(&:attribute).join(',')}
30
- MSG
31
- raise(UsineError::SequenceNotFound, message)
32
- end
33
- end
34
-
35
- def sequence(name, initial_value = 1, &block)
36
- @scoped_sequences << Sequence.new(name, initial_value, &block)
37
- end
38
- end
39
- end
@@ -1,11 +0,0 @@
1
- class UsineError < StandardError
2
- end
3
-
4
- class UsineError::DefinitionNotFound < UsineError
5
- end
6
-
7
- class UsineError::SequenceNotFound < UsineError
8
- end
9
-
10
- class UsineError::SequenceInvalidInitialValue < UsineError
11
- end
@@ -1,13 +0,0 @@
1
- module Usine
2
- class Factory
3
- attr_reader :operation
4
- attr_reader :attributes
5
- attr_reader :block
6
-
7
- def initialize(operation, attributes = {}, &block)
8
- @operation = operation
9
- @attributes = attributes
10
- @block = block
11
- end
12
- end
13
- end
@@ -1,73 +0,0 @@
1
- module Usine
2
- class FactoryEvaluator
3
- attr_reader :mode
4
- attr_reader :attributes
5
- attr_reader :generated_attributes
6
- attr_reader :factory
7
-
8
- def self.call(factory, mode, attributes = {})
9
- factory = new(factory, mode, attributes)
10
- factory.process!
11
- end
12
-
13
- def definitions=(definitions)
14
- @definitions = definitions
15
- end
16
-
17
- def definitions
18
- @definitions ||= Usine.definitions
19
- end
20
-
21
- def sequences=(sequences)
22
- @sequences = sequences
23
- end
24
-
25
- def sequences
26
- @sequences ||= Usine.sequences
27
- end
28
-
29
- def initialize(factory, mode, attributes = {})
30
- @mode = mode
31
- @factory = factory
32
- @attributes = attributes
33
- @generated_attributes = {}
34
- end
35
-
36
- def use(definition_name, *args)
37
- definition = definitions.detect do |definition|
38
- definition.name == definition_name
39
- end
40
-
41
- if definition.nil?
42
- message = <<-MSG
43
- Couldn’t find a definition named: #{definition_name}
44
- Available definitions: #{definitions.map(&:name).join(',')}
45
- MSG
46
- raise(UsineError::DefinitionNotFound, message)
47
- end
48
-
49
- evaluated_definition = DefinitionEvaluator.new(sequences)
50
- evaluated_definition.instance_eval(&definition.block)
51
- evaluated_definition.attributes
52
- end
53
-
54
- def method_missing(method_symbol, *args, &block)
55
- if block
56
- @generated_attributes[method_symbol] = self.instance_eval(&block)
57
- else
58
- @generated_attributes[method_symbol] = use(method_symbol, *args)
59
- end
60
- end
61
-
62
- def process!
63
- self.instance_eval(&factory.block)
64
- @factory.operation.send(@mode, merged_attributes)
65
- end
66
-
67
- protected
68
-
69
- def merged_attributes
70
- Utils.merge_hashes(@generated_attributes, @attributes)
71
- end
72
- end
73
- end
@@ -1,21 +0,0 @@
1
- module Usine
2
- class Sequence
3
- attr_reader :attribute
4
-
5
- def initialize(attribute, *args, &block)
6
- @attribute = attribute
7
- @block = block
8
- @value = args.first || 1
9
-
10
- unless @value.respond_to?(:next)
11
- raise(UsineError::SequenceInvalidInitialValue, "Invalid initial value, it must respond to #next")
12
- end
13
- end
14
-
15
- def next
16
- @block.call(@value)
17
- ensure
18
- @value = @value.next
19
- end
20
- end
21
- end