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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eaa8ad4915b1a30d7e653060e3b9bf1e245e5933d849bb8e7f62e544295907fc
4
- data.tar.gz: 8aa97304d8dcfd2d0c182d31631713cb2b25b530059057c59c24287555fd393e
3
+ metadata.gz: 025e6e1599389ad3a3a99e88695e7c02ed188d39238fe1abb5e9b54ca8e5fba0
4
+ data.tar.gz: a9dbf6f15624dd24fed3862e8fc81180aa59fa19c115382611d76e176dc59d3a
5
5
  SHA512:
6
- metadata.gz: 443583cf5aeb8cd4a888237a13cec6077b6f1aad57a0187da47420a3ba4c28669e5e7bb6c3773bd8410192be58e26293fd2e3bb22a0564ef56aa76808ca233c3
7
- data.tar.gz: 1169e0c5495a9b5f609864d1d64ad76dc380d7f9579707a3e98f0622fe79be7ccde0adacf27417ac560915f3069348f491a9015936aac4bbc7955d8744b4c388
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.1)
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
 
@@ -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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Toritori
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.2'
5
5
  end
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.1
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-12-31 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: