toritori 0.2.1 → 0.2.2
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/Gemfile.lock +1 -1
- data/README.md +113 -68
- data/lib/toritori/factory.rb +3 -2
- data/lib/toritori/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 025e6e1599389ad3a3a99e88695e7c02ed188d39238fe1abb5e9b54ca8e5fba0
|
4
|
+
data.tar.gz: a9dbf6f15624dd24fed3862e8fc81180aa59fa19c115382611d76e176dc59d3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91905f1f599cd3a3383491dbb810c6c0d96580b67c8b552ed56b1acf29bda2bd88a83e1694197b1c31431bb267a2d04a7cad657e4ca7dc00f370f6073f4e5465
|
7
|
+
data.tar.gz: f202f003ffef55921d92bfd04d90fda51c63e90ff959d0b38c365a1c761e3f4c2ddf99e17aa89abce0ec8ca5a01b69d050bf90715bf53f7278f2ecf3470e90b6
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://codeclimate.com/github/andriy-baran/toritori/maintainability)
|
4
4
|
[](https://codeclimate.com/github/andriy-baran/toritori/test_coverage)
|
5
|
+
[](https://badge.fury.io/rb/toritori)
|
5
6
|
|
6
7
|
Simple tool to work with Abstract Factories.
|
7
8
|
It provides the DSL for defining a set factories and produce objects.
|
@@ -22,118 +23,162 @@ Or install it yourself as:
|
|
22
23
|
|
23
24
|
$ gem install toritori
|
24
25
|
|
25
|
-
##
|
26
|
-
|
27
|
-
### Basics
|
28
|
-
Add the module to the class
|
26
|
+
## Basic usage
|
27
|
+
First, add module to target class and define factory method
|
29
28
|
```ruby
|
30
29
|
require 'toritori'
|
31
30
|
|
32
31
|
class MyAbstractFactory
|
33
32
|
include Toritori
|
33
|
+
|
34
|
+
factory :chair
|
34
35
|
end
|
35
36
|
```
|
36
|
-
|
37
|
+
You'll get a few methods:
|
37
38
|
```ruby
|
38
|
-
#
|
39
|
-
MyAbstractFactory.
|
40
|
-
|
41
|
-
#
|
42
|
-
MyAbstractFactory.factories
|
43
|
-
#
|
44
|
-
|
45
|
-
MyAbstractFactory.
|
39
|
+
# top level method similar to FactoryBot
|
40
|
+
MyAbstractFactory.create(:chair)
|
41
|
+
|
42
|
+
# a way for inspecting definitions of factory methods
|
43
|
+
MyAbstractFactory.factories # => { chair: #<Toritori::Factory @name: :chair> }
|
44
|
+
MyAbstractFactory.factories[:chair] # => chair: #<Toritori::Factory @name: :chair>
|
45
|
+
# an alias methods that reads from factories hash
|
46
|
+
factory = MyAbstractFactory.chair_factory # => chair: #<Toritori::Factory @name: :chair>
|
47
|
+
|
48
|
+
# Specific factory actually creates new objects
|
49
|
+
# top level method just calls it
|
50
|
+
MyAbstractFactory.factories[:chair].create
|
51
|
+
|
52
|
+
factory.base_class #=> #<Class>
|
53
|
+
factory.creation_method #=> :new
|
46
54
|
```
|
47
|
-
|
55
|
+
This example above shows a rare case when we just create instances of anonymous class.
|
56
|
+
## Setup options
|
57
|
+
In most cases we want to specify a class of objects we are aiming to produce. Or define a few methods ourself.
|
58
|
+
Assume we have some class
|
48
59
|
```ruby
|
49
|
-
|
60
|
+
class Table < Struct(:width, :height, :depth)
|
61
|
+
# ...omitted...
|
62
|
+
end
|
50
63
|
```
|
51
|
-
To
|
64
|
+
To specify that we want to produce instances of `Table` class
|
52
65
|
```ruby
|
53
|
-
Sofa = Struct.new(:width)
|
54
|
-
|
55
66
|
class MyAbstractFactory
|
56
|
-
|
57
|
-
|
58
|
-
factory :sofa, produces: Sofa
|
67
|
+
factory :table, produces: Table
|
59
68
|
end
|
60
69
|
|
61
|
-
MyAbstractFactory.
|
62
|
-
|
70
|
+
factory = MyAbstractFactory.table_factory
|
71
|
+
factory.base_class #=> #<Table>
|
72
|
+
factory.creation_method #=> :new
|
73
|
+
|
74
|
+
MyAbstractFactory.create(:table, 80, 80, 120) #=> #<Table @width=80 @height=80 @depth=180>
|
63
75
|
```
|
64
|
-
|
76
|
+
By default, we just call `new` method to instantiate an object. Some times it's not possible
|
65
77
|
```ruby
|
66
78
|
class MyAbstractFactory
|
67
|
-
include Toritori
|
68
|
-
|
69
79
|
factory :file, produces: File, creation_method: :open
|
70
80
|
end
|
71
81
|
|
82
|
+
factory = MyAbstractFactory.file_factory
|
83
|
+
factory.base_class #=> #<File>
|
84
|
+
factory.creation_method #=> :open
|
85
|
+
|
72
86
|
MyAbstractFactory.file_factory.create('/dev/null') # => #<File @path='/dev/null'>
|
73
87
|
```
|
74
|
-
|
75
|
-
For example:
|
88
|
+
Or you need to use another `factory method`
|
76
89
|
```ruby
|
77
|
-
class
|
78
|
-
|
79
|
-
|
80
|
-
|
90
|
+
class MyAbstractFactory
|
91
|
+
factory :user, produces: User do |**kw|
|
92
|
+
FactoryBot.create(:user, **kw)
|
93
|
+
end
|
81
94
|
end
|
95
|
+
|
96
|
+
factory = MyAbstractFactory.user_factory
|
97
|
+
factory.base_class #=> #<User>
|
98
|
+
factory.creation_method #=> #<Proc>
|
82
99
|
```
|
83
|
-
|
100
|
+
## Sub-classes
|
101
|
+
But the main feature of the library is a possibility to change or extend the produced object. It is achieved by defining a sub-class of the target class. That is why `produces` option is required in most cases
|
84
102
|
```ruby
|
85
|
-
class
|
86
|
-
|
87
|
-
|
88
|
-
@wifi = true
|
89
|
-
end
|
103
|
+
class MyAbstractFactory
|
104
|
+
table_factory.subclass do
|
105
|
+
attr_reader :shape
|
90
106
|
|
91
|
-
|
107
|
+
def initialize(width, height, depth, shape)
|
108
|
+
super(width, height, depth)
|
109
|
+
@shape = shape
|
110
|
+
end
|
92
111
|
end
|
93
112
|
end
|
94
113
|
|
95
|
-
|
96
|
-
|
97
|
-
modern_sofa.add_wifi
|
98
|
-
modern_sofa.wifi # => true
|
114
|
+
MyAbstractFactory.create(:table, 80, 80, 80, :round)
|
115
|
+
#=> #<Table @width=80 @height=80 @depth=180 @shape=:round>
|
99
116
|
```
|
100
|
-
|
117
|
+
However this operation alters a definition of factory
|
101
118
|
```ruby
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
119
|
+
MyAbstractFactory.table_factory.base_class #=> #<Class>
|
120
|
+
MyAbstractFactory.table_factory.base_class.superclass #=> #<Table>
|
121
|
+
MyAbstractFactory.table_factory.creation_method #=> :new
|
122
|
+
```
|
123
|
+
Sometimes when sub-class definition is big it is better to put it into a separate file.
|
124
|
+
```ruby
|
125
|
+
class ModernTable < Table
|
126
|
+
# ... omitted ...
|
127
|
+
end
|
109
128
|
|
110
|
-
|
111
|
-
|
129
|
+
# Alternatively more generic code
|
130
|
+
class ModernTable < MyAbstractFactory.table_factory.base_class; end
|
131
|
+
|
132
|
+
class MyAbstractFactory
|
133
|
+
table_factory.subclass produces: ModernTable
|
112
134
|
end
|
113
135
|
|
114
|
-
|
115
|
-
|
136
|
+
MyAbstractFactory.table_factory.base_class #=> #<ModernTable>
|
137
|
+
MyAbstractFactory.table_factory.base_class.superclass #=> #<Table>
|
138
|
+
MyAbstractFactory.table_factory.creation_method #=> :new
|
116
139
|
```
|
117
|
-
|
118
|
-
|
119
|
-
Sometimes when subclass definition is big it is better to put it into a separate file. To make the library to use that sub-class:
|
140
|
+
Note, that you should provide a child class otherwise you'll get exception `Toritori::SubclassError`
|
141
|
+
It is possible to change creation method of a sub-class
|
120
142
|
```ruby
|
121
|
-
class
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
class ModernFactory < MyAbstractFactory
|
126
|
-
# Update initialize method
|
127
|
-
table_factory.subclass(produces: ModernTable, creation_method: :produce) do
|
128
|
-
def self.produce(...)
|
143
|
+
class MyAbstractFactory
|
144
|
+
table_factory.subclass creation_method: :create do
|
145
|
+
def self.create(...)
|
129
146
|
new(...)
|
130
147
|
end
|
131
148
|
end
|
149
|
+
end
|
150
|
+
|
151
|
+
MyAbstractFactory.table_factory.base_class #=> #<Class>
|
152
|
+
MyAbstractFactory.table_factory.base_class.superclass #=> #<Table>
|
153
|
+
MyAbstractFactory.table_factory.creation_method #=> :create
|
154
|
+
```
|
155
|
+
Following calls of `subclass` method will create new sub-class based on sub-class generated by previous invocation.
|
156
|
+
## Inheritance
|
157
|
+
Let's imagine you need to the following setup
|
158
|
+
```ruby
|
159
|
+
class MyAbstractFactory
|
160
|
+
factory :chair
|
161
|
+
factory :table
|
162
|
+
end
|
132
163
|
|
133
|
-
|
164
|
+
class ModernFactory < MyAbstractFactory
|
165
|
+
chair.subclass do
|
166
|
+
include ModernStyle
|
167
|
+
end
|
168
|
+
end
|
169
|
+
class VictorianFactory < MyAbstractFactory
|
170
|
+
chair.subclass do
|
171
|
+
include VictorianStyle
|
172
|
+
end
|
134
173
|
end
|
135
174
|
```
|
136
|
-
|
175
|
+
During inheritance child classes should get the same factories as parent class. Definition of factories is copied from parent to child class
|
176
|
+
```ruby
|
177
|
+
copy = MyAbstractFactory.chair_factory.copy
|
178
|
+
copy.base_class == MyAbstractFactory.chair_factory.base_class
|
179
|
+
copy.creation_method #=> :new
|
180
|
+
```
|
181
|
+
It means that after the inheritance `factories` of child and parent are disconnected. Changes in factory definition in parent factory won't affect child classes and vice versa.
|
137
182
|
|
138
183
|
## Development
|
139
184
|
|
data/lib/toritori/factory.rb
CHANGED
@@ -9,10 +9,10 @@ module Toritori
|
|
9
9
|
self.class.new(@name, base_class: @base_class, creation_method: @creation_method)
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize(name, base_class: nil, creation_method: :new)
|
12
|
+
def initialize(name, base_class: nil, creation_method: :new, &block)
|
13
13
|
@name = name
|
14
14
|
@base_class = base_class
|
15
|
-
@creation_method = creation_method
|
15
|
+
@creation_method = block || creation_method
|
16
16
|
end
|
17
17
|
|
18
18
|
def subclass(produces: nil, creation_method: @creation_method, &block)
|
@@ -22,6 +22,7 @@ module Toritori
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def create(*args, **kwargs, &block)
|
25
|
+
return @creation_method.call(*args, **kwargs, &block) if defined? @creation_method.call
|
25
26
|
return @base_class.new(*args, **kwargs, &block) if @creation_method == :new
|
26
27
|
|
27
28
|
@base_class.public_send(@creation_method, *args, **kwargs, &block)
|
data/lib/toritori/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: toritori
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrii Baran
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Create factories with DSL
|
14
14
|
email:
|