usine 1.1.0 → 1.2.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: 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