Skip to content

Commit 77556b0

Browse files
committed
Merge pull request #4 from dry-rb/container-and-mixin
Split extraction to mixin and contaner versions with reference to #3
2 parents b14b997 + cd839c7 commit 77556b0

26 files changed

+154
-84
lines changed

README.md

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ $ gem install dry-initializer
4242
require 'dry-initializer'
4343

4444
class User
45-
extend Dry::Initializer
45+
extend Dry::Initializer::Mixin
4646

4747
# Params of the initializer along with corresponding readers
4848
param :name, type: String
@@ -76,13 +76,51 @@ class User
7676
end
7777
```
7878

79+
### Container Version
80+
81+
Instead of extending a class with the `Dry::Initializer::Mixin`, you can include a container with the initializer:
82+
83+
```ruby
84+
require 'dry-initializer'
85+
86+
class User
87+
# notice `{}` syntax for the block, not `do..end`
88+
include Dry::Initializer.define {
89+
param :name, type: String
90+
param :role, default: proc { 'customer' }
91+
option :admin, default: proc { false }
92+
}
93+
end
94+
```
95+
96+
Now you do not pollute a class with new variables, but isolate them in a special "container" module with the initializer and attribute readers. This method should be preferred when you don't need subclassing.
97+
98+
If you still need the DSL (`param` and `option`) to be inherited, use the direct extension:
99+
100+
```ruby
101+
require 'dry-initializer'
102+
103+
class BaseService
104+
extend Dry::Initializer::Mixin
105+
alias_method :dependency, :param
106+
end
107+
108+
class ShowUser < BaseService
109+
dependency :user
110+
111+
def call
112+
puts user&.name
113+
end
114+
end
115+
```
116+
79117
### Params and Options
80118

81119
Use `param` to define plain argument:
82120

83121
```ruby
84122
class User
85-
extend Dry::Initializer
123+
extend Dry::Initializer::Mixin
86124

87125
param :name
88126
param :email
@@ -97,7 +135,7 @@ Use `option` to define named (hash) argument:
97135

98136
```ruby
99137
class User
100-
extend Dry::Initializer
138+
extend Dry::Initializer::Mixin
101139

102140
option :name
103141
option :email
@@ -112,7 +150,7 @@ All names should be unique:
112150

113151
```ruby
114152
class User
115-
extend Dry::Initializer
153+
extend Dry::Initializer::Mixin
116154

117155
param :name
118156
option :name # => raises #<SyntaxError ...>
@@ -125,7 +163,7 @@ By default both params and options are mandatory. Use `:default` key to make the
125163

126164
```ruby
127165
class User
128-
extend Dry::Initializer
166+
extend Dry::Initializer::Mixin
129167

130168
param :name, default: proc { 'Unknown user' }
131169
option :email, default: proc { 'unknown@example.com' }
@@ -144,7 +182,7 @@ Set `nil` as a default value explicitly:
144182

145183
```ruby
146184
class User
147-
extend Dry::Initializer
185+
extend Dry::Initializer::Mixin
148186

149187
param :name
150188
option :email, default: proc { nil }
@@ -163,7 +201,7 @@ If you need to **assign** proc as a default value, wrap it to another one:
163201

164202
```ruby
165203
class User
166-
extend Dry::Initializer
204+
extend Dry::Initializer::Mixin
167205

168206
param :name_proc, default: proc { proc { 'Unknown user' } }
169207
end
@@ -176,7 +214,7 @@ Proc will be executed in a scope of new instance. You can refer to other argumen
176214

177215
```ruby
178216
class User
179-
extend Dry::Initializer
217+
extend Dry::Initializer::Mixin
180218

181219
param :name
182220
param :email, default: proc { "#{name.downcase}@example.com" }
@@ -190,7 +228,7 @@ user.email # => 'andrew@example.com'
190228

191229
```ruby
192230
class User
193-
extend Dry::Initializer
231+
extend Dry::Initializer::Mixin
194232

195233
param :name, default: -> (obj) { 'Dude' }
196234
end
@@ -204,7 +242,7 @@ You cannot define required parameter after optional ones. The following example
204242

205243
```ruby
206244
class User
207-
extend Dry::Initializer
245+
extend Dry::Initializer::Mixin
208246

209247
param :name, default: proc { 'Unknown name' }
210248
param :email # => #<SyntaxError ...>
@@ -217,7 +255,7 @@ To set type constraint use `:type` key:
217255

218256
```ruby
219257
class User
220-
extend Dry::Initializer
258+
extend Dry::Initializer::Mixin
221259

222260
param :name, type: String
223261
end
@@ -233,7 +271,7 @@ You can use plain Ruby classes and modules as type constraint (see above), or us
233271

234272
```ruby
235273
class User
236-
extend Dry::Initializer
274+
extend Dry::Initializer::Mixin
237275

238276
param :name, type: Dry::Types::Coercion::String
239277
end
@@ -243,7 +281,7 @@ Or you can define custom constraint as a proc:
243281

244282
```ruby
245283
class User
246-
extend Dry::Initializer
284+
extend Dry::Initializer::Mixin
247285

248286
param :name, type: proc { |v| raise TypeError if String === v }
249287
end
@@ -262,7 +300,7 @@ To skip the reader, use `reader: false`:
262300

263301
```ruby
264302
class User
265-
extend Dry::Initializer
303+
extend Dry::Initializer::Mixin
266304

267305
param :name
268306
param :email, reader: false
@@ -283,7 +321,7 @@ Subclassing preserves all definitions being made inside a superclass:
283321

284322
```ruby
285323
class User
286-
extend Dry::Initializer
324+
extend Dry::Initializer::Mixin
287325

288326
param :name
289327
end

benchmarks/options.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,28 @@
22

33
require "dry-initializer"
44
class NoOptsTest
5-
extend Dry::Initializer
5+
extend Dry::Initializer::Mixin
66

77
param :foo
88
option :bar
99
end
1010

1111
class DefaultsTest
12-
extend Dry::Initializer
12+
extend Dry::Initializer::Mixin
1313

1414
param :foo, default: proc { "FOO" }
1515
option :bar, default: proc { "BAR" }
1616
end
1717

1818
class TypesTest
19-
extend Dry::Initializer
19+
extend Dry::Initializer::Mixin
2020

2121
param :foo, type: String
2222
option :bar, type: String
2323
end
2424

2525
class DefaultsAndTypesTest
26-
extend Dry::Initializer
26+
extend Dry::Initializer::Mixin
2727

2828
param :foo, type: String, default: proc { "FOO" }
2929
option :bar, type: String, default: proc { "BAR" }

benchmarks/params.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def initialize(foo, bar)
1313

1414
require "dry-initializer"
1515
class DryTest
16-
extend Dry::Initializer
16+
extend Dry::Initializer::Mixin
1717

1818
param :foo
1919
param :bar

benchmarks/params_vs_options.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
require "dry-initializer"
44

55
class ParamDefaults
6-
extend Dry::Initializer
6+
extend Dry::Initializer::Mixin
77

88
param :foo, default: proc { "FOO" }
99
param :bar, default: proc { "BAR" }
1010
param :baz, default: proc { "BAZ" }
1111
end
1212

1313
class OptionDefaults
14-
extend Dry::Initializer
14+
extend Dry::Initializer::Mixin
1515

1616
option :foo, default: proc { "FOO" }
1717
option :bar, default: proc { "BAR" }

benchmarks/several_defaults.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,31 @@
22

33
require "dry-initializer"
44
class WithoutDefaults
5-
extend Dry::Initializer
5+
extend Dry::Initializer::Mixin
66

77
param :foo
88
param :bar
99
param :baz
1010
end
1111

1212
class WithOneDefault
13-
extend Dry::Initializer
13+
extend Dry::Initializer::Mixin
1414

1515
param :foo
1616
param :bar
1717
param :baz, default: proc { "BAZ" }
1818
end
1919

2020
class WithTwoDefaults
21-
extend Dry::Initializer
21+
extend Dry::Initializer::Mixin
2222

2323
param :foo
2424
param :bar, default: proc { "BAR" }
2525
param :baz, default: proc { "BAZ" }
2626
end
2727

2828
class WithThreeDefaults
29-
extend Dry::Initializer
29+
extend Dry::Initializer::Mixin
3030

3131
param :foo, default: proc { "FOO" }
3232
param :bar, default: proc { "BAR" }

benchmarks/with_defaults.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def initialize(foo: "FOO", bar: "BAR")
1111

1212
require "dry-initializer"
1313
class DryTest
14-
extend Dry::Initializer
14+
extend Dry::Initializer::Mixin
1515

1616
option :foo, default: proc { "FOO" }
1717
option :bar, default: proc { "BAR" }

benchmarks/with_types.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def initialize(foo:, bar:)
1313

1414
require "dry-initializer"
1515
class DryTest
16-
extend Dry::Initializer
16+
extend Dry::Initializer::Mixin
1717

1818
option :foo, type: String
1919
option :bar, type: String

benchmarks/with_types_and_defaults.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def initialize(foo: "FOO", bar: "BAR")
1313

1414
require "dry-initializer"
1515
class DryTest
16-
extend Dry::Initializer
16+
extend Dry::Initializer::Mixin
1717

1818
option :foo, type: String, default: proc { "FOO" }
1919
option :bar, type: String, default: proc { "BAR" }

benchmarks/without_options.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def initialize(foo:, bar:)
1111

1212
require "dry-initializer"
1313
class DryTest
14-
extend Dry::Initializer
14+
extend Dry::Initializer::Mixin
1515

1616
option :foo
1717
option :bar

dry-initializer.gemspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Gem::Specification.new do |gem|
1313

1414
gem.required_ruby_version = ">= 2.2"
1515

16-
gem.add_development_dependency "guard-rspec", "~> 4.0"
17-
gem.add_development_dependency "rake", "~> 10.5"
16+
gem.add_development_dependency "rspec", "~> 3.0"
17+
gem.add_development_dependency "rake", "~> 10.0"
1818
gem.add_development_dependency "dry-types", "~> 0.5.1"
1919
end

lib/dry/initializer.rb

Lines changed: 5 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,45 +9,13 @@ module Initializer
99
require_relative "initializer/argument"
1010
require_relative "initializer/arguments"
1111
require_relative "initializer/builder"
12+
require_relative "initializer/mixin"
1213

13-
# Declares a plain argument
14-
#
15-
# @param [#to_sym] name
16-
#
17-
# @option options [Object] :default The default value
18-
# @option options [#call] :type The type constraings via `dry-types`
19-
# @option options [Boolean] :reader (true) Whether to define attr_reader
20-
#
21-
# @return [self] itself
22-
#
23-
def param(name, **options)
24-
arguments_builder.define_initializer(name, option: false, **options)
25-
self
26-
end
27-
28-
# Declares a named argument
29-
#
30-
# @param (see #param)
31-
# @option (see #param)
32-
# @return (see #param)
33-
#
34-
def option(name, **options)
35-
arguments_builder.define_initializer(name, option: true, **options)
36-
self
37-
end
38-
39-
private
40-
41-
def arguments_builder
42-
@arguments_builder ||= begin
43-
builder = Builder.new
44-
include builder.mixin
45-
builder
14+
def self.define(&block)
15+
Module.new do |container|
16+
container.extend Dry::Initializer::Mixin
17+
container.instance_eval(&block)
4618
end
4719
end
48-
49-
def inherited(klass)
50-
klass.instance_variable_set(:@arguments_builder, arguments_builder)
51-
end
5220
end
5321
end

0 commit comments

Comments
 (0)