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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47da200bc6b1c00517984ac052c3f3c1f3eab8dcfb9444158a893fb3e254bef4
4
- data.tar.gz: 11da3242909586815d5c564c9ab80b04ccdd66b0cc02b96f61d416758c265c3f
3
+ metadata.gz: 025e6e1599389ad3a3a99e88695e7c02ed188d39238fe1abb5e9b54ca8e5fba0
4
+ data.tar.gz: a9dbf6f15624dd24fed3862e8fc81180aa59fa19c115382611d76e176dc59d3a
5
5
  SHA512:
6
- metadata.gz: 4887c90e93f2a13c8942b6b7028f957dead740214ebdadc4717f71ef5005a7dd672c3559b9523bc52e4e741c94cfba637ec4b27215eb31c1bf003a6a027130fd
7
- data.tar.gz: cf38a85c4d6efd4989f49740dfeb373780325b88007d34bbb18841b7efb6987c924d80516229cf6c17a389471ca772b1eafe26fad792c752b31eda65e68a41bc
6
+ metadata.gz: 91905f1f599cd3a3383491dbb810c6c0d96580b67c8b552ed56b1acf29bda2bd88a83e1694197b1c31431bb267a2d04a7cad657e4ca7dc00f370f6073f4e5465
7
+ data.tar.gz: f202f003ffef55921d92bfd04d90fda51c63e90ff959d0b38c365a1c761e3f4c2ddf99e17aa89abce0ec8ca5a01b69d050bf90715bf53f7278f2ecf3470e90b6
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- toritori (0.2.0)
4
+ toritori (0.2.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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
- ## Usage
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
- It will provide an ablility to declare and refer individual factories
37
+ You'll get a few methods:
37
38
  ```ruby
38
- # Declaration
39
- MyAbstractFactory.factory(:chair)
40
- MyAbstractFactory.factory(:table)
41
- # Access all factories
42
- MyAbstractFactory.factories # => { chair: #<Toritori::Factory @name: :chair>, table: #<Toritori::Factory @name: :table> }
43
- # Access individual factory
44
- MyAbstractFactory.chair_factory # => #<Toritori::Factory @name: :chair>
45
- MyAbstractFactory.table_factory # => #<Toritori::Factory @name: :table>
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
- Creating objects is also easy
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
- MyAbstractFactory.table_factory.create(<attrs>) # => #<Class>
60
+ class Table < Struct(:width, :height, :depth)
61
+ # ...omitted...
62
+ end
50
63
  ```
51
- To provide a specific class for factory to create instances
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
- include Toritori
57
-
58
- factory :sofa, produces: Sofa
67
+ factory :table, produces: Table
59
68
  end
60
69
 
61
- MyAbstractFactory.sofa_factory # =>
62
- MyAbstractFactory.sofa_factory.create(2300) # => #<Sofa @width=2300>
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
- The library defaults is to use `new` method for instantiation and bypass parameters from `create` method. But if you need to customize this behaviour
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
- ### Subclassing
75
- For example:
88
+ Or you need to use another `factory method`
76
89
  ```ruby
77
- class ModernFactory < MyAbstractFactory
78
- factories # => { chair: #<Toritori::Factory @name: :chair>,
79
- # table: #<Toritori::Factory @name: :table>,
80
- # sofa: #<Toritori::Factory @name: :sofa, @base_class=Sofa> }
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
- If we need to add a wifi option to sofa
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 ModernFactory < MyAbstractFactory
86
- sofa_factory.subclass do
87
- def add_wifi
88
- @wifi = true
89
- end
103
+ class MyAbstractFactory
104
+ table_factory.subclass do
105
+ attr_reader :shape
90
106
 
91
- attr_reader :wifi
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
- modern_sofa = ModernFactory.sofa_factory.create(2500)
96
- modern_sofa.wifi # => nil
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
- If we need to add wifi option to initializer
117
+ However this operation alters a definition of factory
101
118
  ```ruby
102
- class ModernFactory < MyAbstractFactory
103
- # Update initialize method
104
- chair_factory.subclass do
105
- def initialize(width, wifi)
106
- super(width)
107
- @wifi = wifi
108
- end
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
- attr_reader :wifi
111
- end
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
- modern_chair = ModernFactory.chair_factory.create(2500, wifi: false)
115
- modern_chair.wifi # => false
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
- The subclass (`ModernFactory`) will gen a copy of `factories` so you can customize sublasses without side effects on a base class (`MyAbstractFactory`).
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 ModernTable < MyAbstractFactory.table_factory.base_class
122
- # ... omitted ...
123
- end
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
- table_factory.create # => #<ModernTable>
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
- The sub-class should be a child of a base class we specified in factory of the parent abstract factory, otherwise you'll get `Toritori::SubclassError`. We recommend to have base classes (`produces:` option) for parent abstract factory defined explicitly to have an ability to refer them in sub-class files.
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
 
@@ -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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Toritori
4
- VERSION = '0.2.0'
4
+ VERSION = '0.2.2'
5
5
  end
data/lib/toritori.rb CHANGED
@@ -28,6 +28,10 @@ module Toritori
28
28
  super
29
29
  subclass.factories = factories.transform_values(&:copy)
30
30
  end
31
+
32
+ def create(name, *args, **kwargs, &block)
33
+ factories[name].create(*args, **kwargs, &block)
34
+ end
31
35
  end
32
36
 
33
37
  def self.included(receiver)
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.0
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: 2023-08-21 00:00:00.000000000 Z
11
+ date: 2024-04-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Create factories with DSL
14
14
  email: