toritori 0.2.0 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +113 -68
- data/lib/toritori/factory.rb +5 -2
- data/lib/toritori/version.rb +1 -1
- data/lib/toritori.rb +4 -0
- 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
|
[![Maintainability](https://api.codeclimate.com/v1/badges/4e5138d5018b81671692/maintainability)](https://codeclimate.com/github/andriy-baran/toritori/maintainability)
|
4
4
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/4e5138d5018b81671692/test_coverage)](https://codeclimate.com/github/andriy-baran/toritori/test_coverage)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/toritori.svg)](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
@@ -3,14 +3,16 @@
|
|
3
3
|
module Toritori
|
4
4
|
# Generates module that adds support for objects creation
|
5
5
|
class Factory
|
6
|
+
attr_reader :base_class, :creation_method
|
7
|
+
|
6
8
|
def copy
|
7
9
|
self.class.new(@name, base_class: @base_class, creation_method: @creation_method)
|
8
10
|
end
|
9
11
|
|
10
|
-
def initialize(name, base_class: nil, creation_method: :new)
|
12
|
+
def initialize(name, base_class: nil, creation_method: :new, &block)
|
11
13
|
@name = name
|
12
14
|
@base_class = base_class
|
13
|
-
@creation_method = creation_method
|
15
|
+
@creation_method = block || creation_method
|
14
16
|
end
|
15
17
|
|
16
18
|
def subclass(produces: nil, creation_method: @creation_method, &block)
|
@@ -20,6 +22,7 @@ module Toritori
|
|
20
22
|
end
|
21
23
|
|
22
24
|
def create(*args, **kwargs, &block)
|
25
|
+
return @creation_method.call(*args, **kwargs, &block) if defined? @creation_method.call
|
23
26
|
return @base_class.new(*args, **kwargs, &block) if @creation_method == :new
|
24
27
|
|
25
28
|
@base_class.public_send(@creation_method, *args, **kwargs, &block)
|
data/lib/toritori/version.rb
CHANGED
data/lib/toritori.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:
|