smart_init 4.1.0 → 5.0.1

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: fedc150bbb8aeed53e1bcc08073e643175a5fbe49f909a20c17cac145a95f969
4
- data.tar.gz: 71c028b8b52b4ef553fd0d6a5711b913bac1fc8d46e4ca41d067b5e4441a5fa1
3
+ metadata.gz: 6a6053427e61f4ef731105e0ec9a96799333b652b82c44b875900d61a1a6a1b3
4
+ data.tar.gz: eb2559ca73adcfe8ad893e9fcf8510ed2bb29229331add6d5f5cdb41b6c99d9e
5
5
  SHA512:
6
- metadata.gz: e1ec743157b4a687d902d08023654604db4c9ec502448313e9eb79f3e621cb0f43fa293fcba5bcd31018e14cfeb8dd149f937ac89a1cadf1ff0bcb28e226751a
7
- data.tar.gz: 681f6bd9c9b9e75580154bde00819894ff3a3f0bbe9598041e21123b4d63284d7d01deda8e374fc462f41d55245c731ee64a70f12380ef2f77f15d2a570ca64f
6
+ metadata.gz: c36fb8e29eb8f2d78814ed27059bbbe5c5d0f828a97421565f47ac8bbea7e153f70407c589a4576dfc6aba2d238b7d2cba51526ac7343c52fccd47d891dfb86f
7
+ data.tar.gz: 3c9b8cc59befe99ea87afc76ec287a4d56a2696b557cb30b2854de9f24a4c79bf1ffdd309e6aacd9317c0a5da95d7c4af8c75200e092a74a0d6b6a56c984906c
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 pawurb
1
+ Copyright (c) 2020 Paweł Urbanek
2
2
 
3
3
  MIT License
4
4
 
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
19
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
20
  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
21
  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Smart Init [![Build Status](https://travis-ci.org/pawurb/smart_init.svg)](https://travis-ci.org/pawurb/smart_init) [![Gem Version](https://badge.fury.io/rb/smart_init.svg)](https://badge.fury.io/rb/smart_init)
1
+ # Smart Init - Simple service objects in Ruby [![Build Status](https://travis-ci.org/pawurb/smart_init.svg)](https://travis-ci.org/pawurb/smart_init) [![Gem Version](https://badge.fury.io/rb/smart_init.svg)](https://badge.fury.io/rb/smart_init)
2
2
 
3
- Do you find yourself writing a lot of boilerplate code like that?
3
+ Do you find yourself writing a lot of boilerplate code like this?
4
4
 
5
5
  ```ruby
6
6
  def initialize(network_provider, api_token)
@@ -13,7 +13,7 @@ def self.call(network_provider, api_token)
13
13
  end
14
14
  ```
15
15
 
16
- Gem provides a simple DSL for getting rid of it. It offers an alternative to using `Struct.new` which does not check for number of parameters provided in initializer, exposes getters and instantiates unecessary class instances.
16
+ This gem provides a simple DSL for getting rid of it. It offers an alternative to using `Struct.new` which does not check for number of parameters provided in initializer, exposes getters and instantiates unecessary class instances.
17
17
 
18
18
  **Smart Init** offers a unified API convention for stateless service objects, accepting values in initializer and exposing one public class method `call` which instantiates new objects and accepts arguments passed to initializer.
19
19
 
@@ -32,6 +32,8 @@ gem 'smart_init'
32
32
  You can use it either by extending a module:
33
33
 
34
34
  ```ruby
35
+ require 'smart_init'
36
+
35
37
  class ApiClient
36
38
  extend SmartInit
37
39
 
@@ -141,23 +143,76 @@ end
141
143
 
142
144
  client = SemiPublicApiClient.new(network_provider: Faraday.new, api_token: 'secret_token')
143
145
  client.network_provider => #<Faraday::Connection:0x000...>
144
- client.api_token => 'secret_token' => # NoMethodError (private method `api_token' called for #<ApiClient:0x000...>)
146
+ client.api_token => 'secret_token' => # NoMethodError (private method `api_token' called for #<SemiPublicApiClient:0x000...>)
145
147
  ```
146
148
 
147
- ## Arguments API
149
+ ### Accessors access
148
150
 
149
- Alternatively you can use an API without hash arguments, default values and public readers support:
151
+ Similarly, this is how it would look if you tried to use a writer method:
150
152
 
151
153
  ```ruby
152
- class Calculator < SmartInit::Base
153
- initialize_with_args :data
154
- is_callable
154
+ client = ApiClient.new(network_provider: Faraday.new, api_token: 'secret_token')
155
+ client.api_token = 'new_token' => # NoMethodError (private method `api_token=' called for #<ApiClient:0x000..>)
156
+ ```
155
157
 
156
- def call
157
- ...
158
- result
159
- end
158
+ Optionally you can make all or subset of accessors public using the `public_accessors` config option. It accepts `true` or an array of method names as an argument. This will provide both reader and writer methods publicly.
159
+
160
+ ```ruby
161
+ class PublicApiClient < SmartInit::Base
162
+ initialize_with :network_provider, :api_token, public_accessors: true
160
163
  end
161
164
 
162
- Calculator.call(data) => result
165
+ client = PublicApiClient.new(network_provider: Faraday.new, api_token: 'secret_token')
166
+ client.network_provider => #<Faraday::Connection:0x000...>
167
+ client.network_provider = Typhoeus::Request.new(...) => #<Typhoeus::Request:0x000...>
168
+ client.api_token => 'secret_token'
169
+ client.api_token = 'new_token' => 'new_token'
170
+ ```
171
+
172
+ ```ruby
173
+ class SemiPublicApiClient < SmartInit::Base
174
+ initialize_with :network_provider, :api_token, public_accessors: [:network_provider]
175
+ end
176
+
177
+ client = SemiPublicApiClient.new(network_provider: Faraday.new, api_token: 'secret_token')
178
+ client.network_provider => #<Faraday::Connection:0x000...>
179
+ client.network_provider = Typhoeus::Request.new(...) => #<Typhoeus::Request:0x000...>
180
+ client.api_token => # NoMethodError (private method `api_token' called for #<SemiPublicApiClient:0x000...>)
181
+ client.api_token = 'new_token' => # NoMethodError (undefined method `api_token=' called for #<SemiPublicApiClient:0x000...>)
182
+ ```
183
+
184
+ Finally, you can mix them together like this:
185
+
186
+ ```ruby
187
+ class PublicReadersSemiPublicAccessorsApiClient < SmartInit::Base
188
+ initialize_with :network_provider, :api_token, :timeout,
189
+ public_readers: true, public_accessors: [:network_provider]
190
+ end
191
+
192
+ client = PublicReadersSemiPublicAccessorsApiClient.new(
193
+ network_provider: Faraday.new, api_token: 'secret_token', timeout_length: 100
194
+ )
195
+ client.network_provider => #<Faraday::Connection:0x000...>
196
+ client.network_provider = Typhoeus::Request.new(...) => #<Typhoeus::Request:0x000...>
197
+ client.api_token => 'secret_token'
198
+ client.api_token = 'new_token' => # NoMethodError (undefined method `api_token=' called for #<SemiPublicApiClient:0x000...>)
199
+ client.timeout_length => 100
200
+ client.timeout_length = 150 => # NoMethodError (undefined method `timeout_length=' called for #<SemiPublicApiClient:0x000...>)
201
+ ```
202
+
203
+ ```ruby
204
+ class SemiPublicReadersSemiPublicAccessorsApiClient < SmartInit::Base
205
+ initialize_with :network_provider, :api_token, :timeout,
206
+ public_readers: [:timeout], public_accessors: [:network_provider]
207
+ end
208
+
209
+ client = SemiPublicReadersSemiPublicAccessorsApiClient.new(
210
+ network_provider: Faraday.new, api_token: 'secret_token', timeout_length: 100
211
+ )
212
+ client.network_provider => #<Faraday::Connection:0x000...>
213
+ client.network_provider = Typhoeus::Request.new(...) => #<Typhoeus::Request:0x000...>
214
+ client.api_token => # NoMethodError (private method `api_token' called for #<SemiPublicReadersSemiPublicAccessorsApiClient:0x000...>)
215
+ client.api_token = 'new_token' => # NoMethodError (undefined method `api_token=' called for #<SemiPublicReadersSemiPublicAccessorsApiClient:0x000...>)
216
+ client.timeout_length => 100
217
+ client.timeout_length = 150 => # NoMethodError (undefined method `timeout_length=' called for #<SemiPublicReadersSemiPublicAccessorsApiClient:0x000...>)
163
218
  ```
data/lib/smart_init.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'smart_init/main'
2
4
 
3
5
  module SmartInit
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SmartInit
2
4
  def is_callable(opts={})
3
5
  method_name = if name_from_opts = opts[:method_name]
@@ -6,46 +8,32 @@ module SmartInit
6
8
  :call
7
9
  end
8
10
 
9
- define_singleton_method method_name do |*parameters|
10
- new(*parameters).public_send(method_name)
11
+ define_singleton_method method_name do |**parameters|
12
+ new(**parameters).public_send(method_name)
11
13
  end
12
14
  end
13
15
 
14
- def initialize_with_hash *attributes
15
- public_readers_filter = -> (el) {
16
- el.is_a?(Hash) && el.keys == [:public_readers]
17
- }
18
- attributes = attributes.map do |el|
19
- if el.is_a?(Hash)
20
- el.map { |k, v| { k => v } }
21
- else
22
- el
23
- end
24
- end.flatten
25
-
26
- public_readers = attributes.select(&public_readers_filter)
27
- attributes.delete_if(&public_readers_filter)
28
- required_attrs = attributes.select { |el| el.is_a?(Symbol) }
29
-
30
- default_value_attrs = attributes.select { |el| el.is_a?(Hash) }.reduce(Hash.new, :merge) || {}
31
-
32
- define_method :initialize do |*parameters|
33
- parameters = [{}] if parameters == []
34
- unless parameters.first.is_a?(Hash)
35
- raise ArgumentError, "invalid input, expected hash of attributes"
36
- end
16
+ def initialize_with_hash(*required_attrs, **attributes_and_options)
17
+ public_readers = attributes_and_options.delete(:public_readers) || []
18
+ public_accessors = attributes_and_options.delete(:public_accessors) || []
19
+ if public_readers == true || public_accessors == true
20
+ public_readers = required_attrs + attributes_and_options.keys
21
+ public_accessors = required_attrs + attributes_and_options.keys if public_accessors == true
22
+ else
23
+ public_readers += public_accessors
24
+ end
37
25
 
26
+ define_method :initialize do |**parameters|
38
27
  required_attrs.each do |required_attr|
39
- unless parameters.first.has_key?(required_attr)
28
+ unless parameters.has_key?(required_attr)
40
29
  raise ArgumentError, "missing required attribute #{required_attr}"
41
30
  end
42
31
  end
43
-
44
- (required_attrs + default_value_attrs.keys).each do |attribute|
45
- value = if parameters.first.has_key?(attribute)
46
- parameters.first.fetch(attribute)
32
+ (required_attrs + attributes_and_options.keys).each do |attribute|
33
+ value = if parameters.has_key?(attribute)
34
+ parameters.fetch(attribute)
47
35
  else
48
- default_value_attrs[attribute]
36
+ attributes_and_options[attribute]
49
37
  end
50
38
 
51
39
  instance_variable_set("@#{attribute}", value)
@@ -53,44 +41,16 @@ module SmartInit
53
41
  end
54
42
 
55
43
  instance_eval do
56
- all_readers = (required_attrs + default_value_attrs.keys).compact
44
+ all_readers = (required_attrs + attributes_and_options.keys)
57
45
  attr_reader(*all_readers)
58
-
59
- if public_readers.count == 0
60
- all_readers.each do |method_name|
61
- private method_name
62
- end
63
- elsif public_readers.first.fetch(:public_readers).is_a?(Array)
64
- (all_readers - public_readers.first.fetch(:public_readers)).each do |method_name|
65
- private method_name
66
- end
46
+ (all_readers - public_readers).each do |reader|
47
+ private reader
67
48
  end
49
+ attr_writer(*public_accessors)
68
50
  end
69
51
  end
70
52
 
71
53
  alias initialize_with initialize_with_hash
72
-
73
- def initialize_with_args *attributes
74
- define_method :initialize do |*parameters|
75
- if attributes.count != parameters.count
76
- raise ArgumentError, "wrong number of arguments (given #{parameters.count}, expected #{attributes.count})"
77
- end
78
-
79
- attributes.zip(parameters).each do |pair|
80
- name = pair[0]
81
- value = pair[1]
82
- instance_variable_set("@#{name}", value)
83
- end
84
- end
85
-
86
- instance_eval do
87
- attr_reader(*attributes)
88
-
89
- attributes.each do |method_name|
90
- private method_name
91
- end
92
- end
93
- end
94
54
  end
95
55
 
96
56
  class SmartInit::Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module SmartInit
2
- VERSION = "4.1.0"
4
+ VERSION = "5.0.1"
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "test/unit"
2
4
  require_relative '../lib/smart_init/main'
3
5
 
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "byebug"
4
+ require "test/unit"
5
+ require_relative '../lib/smart_init/main'
6
+
7
+ class TestAllPublicAccessors
8
+ extend SmartInit
9
+ initialize_with :attribute_1, :attribute_2, public_accessors: true
10
+ is_callable
11
+
12
+ def call
13
+ [attribute_1, attribute_2]
14
+ end
15
+ end
16
+
17
+ class TestSomePublicAccessors
18
+ extend SmartInit
19
+ initialize_with :attribute_1, :attribute_2, public_accessors: [:attribute_1]
20
+ is_callable
21
+
22
+ def call
23
+ [attribute_1, attribute_2]
24
+ end
25
+ end
26
+
27
+ class TestDefaultPublicAccessors
28
+ extend SmartInit
29
+ initialize_with :attribute_1, attribute_2: 2, public_accessors: [:attribute_2]
30
+
31
+ def call
32
+ [attribute_1, attribute_2]
33
+ end
34
+ end
35
+
36
+ class HashApiPublicTestAccessors < Test::Unit::TestCase
37
+ def test_all_public
38
+ service = TestAllPublicAccessors.new(attribute_1: "a", attribute_2: "b")
39
+ assert_nothing_raised do
40
+ service.attribute_1 = "c"
41
+ service.attribute_2 = "d"
42
+ end
43
+ assert_equal service.attribute_1, "c"
44
+ assert_equal service.attribute_2, "d"
45
+ end
46
+
47
+ def test_some_public
48
+ service = TestSomePublicAccessors.new(attribute_1: "a", attribute_2: "b")
49
+ assert_nothing_raised do
50
+ service.attribute_1 = "c"
51
+ end
52
+ assert_equal service.attribute_1, "c"
53
+ assert_raise NoMethodError do
54
+ service.attribute_2
55
+ end
56
+ assert_raise NoMethodError do
57
+ service.attribute_2 = "d"
58
+ end
59
+ end
60
+
61
+ def test_default_public
62
+ service = TestDefaultPublicAccessors.new(attribute_1: "a")
63
+ assert_nothing_raised do
64
+ service.attribute_2 = 3
65
+ end
66
+ assert_equal service.attribute_2, 3
67
+ assert_raise NoMethodError do
68
+ service.attribute_1 = "b"
69
+ end
70
+ assert_raise NoMethodError do
71
+ service.attribute_1
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "byebug"
4
+ require "test/unit"
5
+ require_relative '../lib/smart_init/main'
6
+
7
+ class TestSomePublicMixed
8
+ extend SmartInit
9
+ initialize_with :attribute_1, :attribute_2, :attribute_3, :attribute_4,
10
+ public_readers: [:attribute_1],
11
+ public_accessors: [:attribute_2, :attribute_3]
12
+ is_callable
13
+
14
+ def call
15
+ [attribute_1, attribute_2, attribute_3, attribute_4]
16
+ end
17
+ end
18
+
19
+ class TestAllReadersSomeAccessorsPublic
20
+ extend SmartInit
21
+ initialize_with :attribute_1, :attribute_2, public_readers: true, public_accessors: [:attribute_2]
22
+
23
+ def call
24
+ [attribute_1, attribute_2]
25
+ end
26
+ end
27
+
28
+ class HashApiPublicTest < Test::Unit::TestCase
29
+
30
+ def test_readers_some_public_mixed
31
+ service = TestSomePublicMixed.new(
32
+ attribute_1: "a", attribute_2: "b",
33
+ attribute_3: "c", attribute_4: "d"
34
+ )
35
+ assert_nothing_raised do
36
+ service.attribute_1
37
+ service.attribute_2
38
+ service.attribute_3
39
+ end
40
+ assert_raise NoMethodError do
41
+ service.attribute_4
42
+ end
43
+ end
44
+
45
+ def test_writers_some_public_mixed
46
+ service = TestSomePublicMixed.new(
47
+ attribute_1: "a", attribute_2: "b",
48
+ attribute_3: "c", attribute_4: "d"
49
+ )
50
+ assert_nothing_raised do
51
+ service.attribute_2 = "e"
52
+ service.attribute_3 = "f"
53
+ end
54
+ assert_equal service.attribute_2, "e"
55
+ assert_equal service.attribute_3, "f"
56
+ assert_raise NoMethodError do
57
+ service.attribute_4 = "g"
58
+ end
59
+ end
60
+
61
+ def test_readers_all_readers_some_accessors_public
62
+ service = TestAllReadersSomeAccessorsPublic.new(
63
+ attribute_1: "a", attribute_2: "b"
64
+ )
65
+ assert_nothing_raised do
66
+ service.attribute_1
67
+ service.attribute_2
68
+ end
69
+ end
70
+
71
+ def test_writers_all_readers_some_accessors_public
72
+ service = TestAllReadersSomeAccessorsPublic.new(
73
+ attribute_1: "a", attribute_2: "b"
74
+ )
75
+ assert_raise NoMethodError do
76
+ service.attribute_1 = "c"
77
+ end
78
+ assert_nothing_raised do
79
+ service.attribute_2 = "d"
80
+ end
81
+ assert_equal service.attribute_2, "d"
82
+ end
83
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "byebug"
2
4
  require "test/unit"
3
5
  require_relative '../lib/smart_init/main'
@@ -31,6 +33,15 @@ class TestDefaultPublic
31
33
  end
32
34
  end
33
35
 
36
+ class TestDefaultAllPublic
37
+ extend SmartInit
38
+ initialize_with :attribute_1, attribute_2: 2, public_readers: true
39
+
40
+ def call
41
+ [attribute_1, attribute_2]
42
+ end
43
+ end
44
+
34
45
  class HashApiPublicTest < Test::Unit::TestCase
35
46
  def test_all_public
36
47
  service = TestAllPublic.new(attribute_1: "a", attribute_2: "b")
@@ -54,5 +65,11 @@ class HashApiPublicTest < Test::Unit::TestCase
54
65
  service.attribute_1
55
66
  end
56
67
  end
68
+
69
+ def test_default_all_public
70
+ service = TestDefaultAllPublic.new(attribute_1: "a")
71
+ assert_equal service.attribute_1, "a"
72
+ assert_equal service.attribute_2, 2
73
+ end
57
74
  end
58
75
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_init
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 5.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - pawurb
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-25 00:00:00.000000000 Z
11
+ date: 2021-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -69,14 +69,15 @@ files:
69
69
  - lib/smart_init/main.rb
70
70
  - lib/smart_init/version.rb
71
71
  - smart_init.gemspec
72
- - test/test_args_api.rb
73
72
  - test/test_hash_api.rb
73
+ - test/test_hash_public_accessors.rb
74
+ - test/test_hash_public_mixed.rb
74
75
  - test/test_hash_public_readers.rb
75
76
  homepage: http://github.com/pawurb/smart_init
76
77
  licenses:
77
78
  - MIT
78
79
  metadata: {}
79
- post_install_message:
80
+ post_install_message:
80
81
  rdoc_options: []
81
82
  require_paths:
82
83
  - lib
@@ -91,11 +92,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
92
  - !ruby/object:Gem::Version
92
93
  version: '0'
93
94
  requirements: []
94
- rubygems_version: 3.0.6
95
- signing_key:
95
+ rubygems_version: 3.1.6
96
+ signing_key:
96
97
  specification_version: 4
97
98
  summary: Remove Ruby initializer boilerplate code
98
99
  test_files:
99
- - test/test_args_api.rb
100
100
  - test/test_hash_api.rb
101
+ - test/test_hash_public_accessors.rb
102
+ - test/test_hash_public_mixed.rb
101
103
  - test/test_hash_public_readers.rb
@@ -1,77 +0,0 @@
1
- require "test/unit"
2
- require_relative '../lib/smart_init/main'
3
-
4
- class TestClass
5
- extend SmartInit
6
- initialize_with_args :attribute_1, :attribute_2
7
- is_callable
8
-
9
- def call
10
- [attribute_1, attribute_2]
11
- end
12
- end
13
-
14
- class TestNoInit
15
- extend SmartInit
16
- is_callable
17
-
18
- def call
19
- 'result'
20
- end
21
- end
22
-
23
- class TestMethodName
24
- extend SmartInit
25
-
26
- is_callable method_name: :run!
27
-
28
- def run!
29
- true
30
- end
31
- end
32
-
33
- def test_object
34
- @_test_object ||= TestClass.new("attr_1_value", "attr_2_value")
35
- end
36
-
37
- class StandardApiTest < Test::Unit::TestCase
38
- def test_number_of_attributes
39
- assert_nothing_raised do
40
- TestClass.new(
41
- "attr_1_value",
42
- "attr_2_value"
43
- )
44
- end
45
-
46
- assert_raise ArgumentError do
47
- TestClass.new(
48
- "attr_1_value"
49
- )
50
- end
51
- end
52
-
53
- def test_instance_variables
54
- assert_equal test_object.instance_variable_get("@attribute_1"), "attr_1_value"
55
- end
56
-
57
- def test_private_getters
58
- error = assert_raise NoMethodError do
59
- test_object.attribute_1
60
- end
61
- assert_match("private method", error.message)
62
-
63
- assert_equal test_object.send(:attribute_1), "attr_1_value"
64
- end
65
-
66
- def test_is_callable
67
- assert_equal TestClass.call("a", "b"), ["a", "b"]
68
- end
69
-
70
- def test_is_callable_no_initializers
71
- assert_equal TestNoInit.call, 'result'
72
- end
73
-
74
- def test_is_callable_method_name
75
- assert_equal TestMethodName.run!, true
76
- end
77
- end