class_composer 0.0.1 → 1.0.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 +4 -4
- data/CHANGELOG.md +8 -19
- data/Gemfile.lock +1 -1
- data/README.md +135 -7
- data/class_composer.gemspec +2 -2
- data/lib/class_composer/default_object.rb +17 -0
- data/lib/class_composer/generator.rb +38 -18
- data/lib/class_composer/version.rb +1 -1
- metadata +6 -5
- data/lib/class_composer/array.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d53fe12431ff8f1dd4c42d7282d11a64637a0c3ab573e24a7d83443a8a9cac2
|
4
|
+
data.tar.gz: 0376e66b2fef6bc976289dd1f4e4517830dd0a63c0e12519730efc9786865c9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b08e706f2f55865e6d4fab779896450dab524b946412ee8e8be8a673b205a8f3ded0a438db6dbce53d8ebc06c6a88a2dc6cba5a4da3bd5e22a599a205ea9cc0f
|
7
|
+
data.tar.gz: bfa9491b16358fc59b221bd5ac847b861c52a6a5793dca4fc59c81a8ceef3e12c1d3148bd8679833e4c5a643e5738788a9647a1619f9aadc2171b9d497bd0f34
|
data/CHANGELOG.md
CHANGED
@@ -1,26 +1,15 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
|
3
|
+
### Versioning
|
4
4
|
|
5
|
-
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
5
|
+
**Major:** x - Major Releases with new features that constitute a breaking change
|
7
6
|
|
8
|
-
|
7
|
+
**Minor:** x.x - Minor changes or features added which are backwords compatible
|
9
8
|
|
10
|
-
|
9
|
+
**Patch:** x.x.x - Patch Updates
|
11
10
|
|
12
|
-
|
13
|
-
- New feature 1
|
14
|
-
- New feature 2
|
11
|
+
# Release Notes
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
### Removed
|
21
|
-
- Removed feature 1
|
22
|
-
- Removed feature 2
|
23
|
-
|
24
|
-
### Fixed
|
25
|
-
- Bug fix 1
|
26
|
-
- Bug fix 2
|
13
|
+
| Date | Version | Description |
|
14
|
+
|---|---|---|
|
15
|
+
| June 2022 | v1.0.0 | Initial launch of Composer
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
# ClassComposer
|
2
2
|
|
3
|
-
|
4
|
-
able to package up your Ruby library into a gem. Put your Ruby code in the file
|
5
|
-
`lib/class_composer`. To experiment with that code, run
|
6
|
-
`bin/console` for an interactive prompt.
|
7
|
-
|
8
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Basic configuration is relatively simple but tedious to do if done multiple times. throughout a project. The intention of `ClassComposer` is to DRY up as much of that configuration as possible to allow you to just write code.
|
9
4
|
|
10
5
|
## Installation
|
11
6
|
|
7
|
+
`ClassComposer` is hosted on RubyGems https://rubygems.org/gems/class_composer
|
8
|
+
|
12
9
|
Add this line to your application's Gemfile:
|
13
10
|
|
14
11
|
```ruby
|
@@ -25,7 +22,138 @@ Or install it yourself as:
|
|
25
22
|
|
26
23
|
## Usage
|
27
24
|
|
28
|
-
|
25
|
+
### Basic
|
26
|
+
|
27
|
+
`add_composer` is the driving method behind the composer gem. It will
|
28
|
+
- Add a setter Method
|
29
|
+
- Add a getter Method
|
30
|
+
- Add instance variable
|
31
|
+
- Add validation Method
|
32
|
+
|
33
|
+
In short, Composer will behave similarly to `attr_accessor`
|
34
|
+
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
require 'class_composer'
|
38
|
+
|
39
|
+
class MyConfigurableClass
|
40
|
+
include ClassComposer::Generator
|
41
|
+
|
42
|
+
ALLOWED_FIBONACCI = [0, 2, 8, 13, 34]
|
43
|
+
|
44
|
+
add_composer :status, allowed: Integer, default: 35
|
45
|
+
add_composer :number, allowed: Integer, default: 0, validator: -> (val) { val < 50 }
|
46
|
+
# when no default is provided, nil will be returned
|
47
|
+
add_composer :fibbonacci, allowed: Array, validator: ->(val) { val.all? {|i| i.is_a?(Integer) } && val.all? { |i| ALLOWED_FIBONACCI.include?(i) } }, invalid_message: ->(val) { "We only allow #{ALLOWED_FIBONACCI} numbers. Received #{val}" }
|
48
|
+
# Allowed can be passed an array of allowed class types
|
49
|
+
add_composer :type, allowed: [Proc, Integer], default: 35
|
50
|
+
end
|
51
|
+
|
52
|
+
instance = MyConfigurableClass.new
|
53
|
+
instance.type
|
54
|
+
=> 35
|
55
|
+
instance.number = 75
|
56
|
+
ClassComposer::ValidatorError: MyConfigurableClass.number failed validation. number is expected to be Integer.
|
57
|
+
from /gem/lib/class_composer/generator.rb:71:in `block in __composer_assignment__`
|
58
|
+
instance.number = 15
|
59
|
+
=> 15
|
60
|
+
instance.number
|
61
|
+
=> 15
|
62
|
+
instance.fibbonacci
|
63
|
+
=> nil
|
64
|
+
instance.fibbonacci = [1,2,3]
|
65
|
+
ClassComposer::ValidatorError: MyConfigurableClass.fibbonacci failed validation. fibbonacci is expected to be [Array]. We only allow [0, 2, 8, 13, 34] numbers. Received [1, 2, 3]
|
66
|
+
from /gem/lib/class_composer/generator.rb:71:in `block in __composer_assignment__`
|
67
|
+
instance.fibbonacci = [0,13,34]
|
68
|
+
=> [0, 13, 34]
|
69
|
+
instance.fibbonacci
|
70
|
+
=> [0, 13, 34]
|
71
|
+
```
|
72
|
+
|
73
|
+
### KWarg Options
|
74
|
+
|
75
|
+
```
|
76
|
+
allowed
|
77
|
+
- Required: True
|
78
|
+
- What: Expected value of the name of the composed method
|
79
|
+
- Type: Array of Class types or Single Class type
|
80
|
+
|
81
|
+
validator
|
82
|
+
- Required: False
|
83
|
+
- What: Custom way to validate the value of the composed method
|
84
|
+
- Type: Proc
|
85
|
+
- Default: ->(_) { true }
|
86
|
+
- By default validation happens on the `allowed` KWARG first and then the passed in validator function. Proc should expect that the type passed in is one of `allowed`
|
87
|
+
|
88
|
+
validation_error_klass
|
89
|
+
- Required: false
|
90
|
+
- What: Class to raise when a validation error occurs from `allowed` KWarg or from the passed in `validator` proc
|
91
|
+
- Type: Class
|
92
|
+
- Default: ClassComposer::ValidatorError
|
93
|
+
|
94
|
+
validation_error_klass
|
95
|
+
- Required: false
|
96
|
+
- What: Class to raise when a errors occur outside of validation. This can be for composer method errors or proc errors during validation
|
97
|
+
- Type: Class
|
98
|
+
- Default: ClassComposer::Error
|
99
|
+
|
100
|
+
default
|
101
|
+
- Required: false
|
102
|
+
- What: This is the default value to set for the composed method
|
103
|
+
- Type: Should match the `allowed` KWarg
|
104
|
+
- Default: nil
|
105
|
+
- Note: When no default value is provided, the return value from the getter will be `nil`. However, this does not mean that NilClass will be an acceptable value during the setter method
|
106
|
+
|
107
|
+
invalid_message
|
108
|
+
- Required: False
|
109
|
+
- What: Message to add to the base invalid setter method
|
110
|
+
- Type: Proc or String
|
111
|
+
- Proc: ->(val) { } # where val is the failed value of the setter method
|
112
|
+
|
113
|
+
```
|
114
|
+
|
115
|
+
### Advanced
|
116
|
+
|
117
|
+
#### Usage with Array as Allowed
|
118
|
+
Arrays are treated special with the composed methods. `ClassComposer` will inject a custom method `<<` so that it can be treated as a regular array with the added benefit of validation still occuring.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
class CustomArrayClass
|
122
|
+
include ClassComposer::Generator
|
123
|
+
|
124
|
+
add_composer :array, allowed: Array, default: [], validator: ->(val) { val.sum < 40 }, invalid_message: ->(val) { "Array sum of [#{val.sum}] must be less than 40" }
|
125
|
+
end
|
126
|
+
|
127
|
+
instance = CustomArrayClass.new
|
128
|
+
instance.array << 1
|
129
|
+
instance.array << 2
|
130
|
+
instance.array
|
131
|
+
=> [1, 2]
|
132
|
+
instance.array << 50
|
133
|
+
ClassComposer::ValidatorError: CustomArrayClass.array failed validation. array is expected to be Array. Array sum of [53] must be less than 40
|
134
|
+
|
135
|
+
```
|
136
|
+
|
137
|
+
#### Usage with complex configuration
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class ComplexDependencies
|
141
|
+
include ClassComposer::Generator
|
142
|
+
|
143
|
+
add_composer :use_scope, allowed: [TrueClass, FalseClass], default: false
|
144
|
+
add_composer :scope, allowed: Proc
|
145
|
+
|
146
|
+
def scope
|
147
|
+
# skip unless use_scope is explicitly set
|
148
|
+
return -> {} unless @use_scope
|
149
|
+
|
150
|
+
# use passed in scope if present
|
151
|
+
# Otherwise default to blank default
|
152
|
+
@scope || -> {}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
```
|
156
|
+
Adding custom methods allows for higher level of complexity. The methods can be used and accessed just as an `attr_accessor` would.
|
29
157
|
|
30
158
|
## Development
|
31
159
|
|
data/class_composer.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ["Matt Taylor"]
|
9
9
|
spec.email = ["mattius.taylor@gmail.com"]
|
10
10
|
|
11
|
-
spec.summary = "
|
12
|
-
spec.description = "
|
11
|
+
spec.summary = "Easily compose a class via inline code or passed in YAML config. Add instance attributes with custom validations that will DRY up your code"
|
12
|
+
spec.description = "Compose configurations for any class."
|
13
13
|
spec.homepage = "https://github.com/matt-taylor/class_composer"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "class_composer/version"
|
4
|
+
|
5
|
+
# This is to add a falsey like default behavior
|
6
|
+
# When default value is not passed in let this be an allowed value
|
7
|
+
# This is intended to eventually be configurable
|
8
|
+
|
9
|
+
module ClassComposer
|
10
|
+
module DefaultObject
|
11
|
+
module_function
|
12
|
+
|
13
|
+
def value
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "class_composer/
|
3
|
+
require "class_composer/default_object"
|
4
4
|
|
5
5
|
module ClassComposer
|
6
6
|
module Generator
|
@@ -13,22 +13,35 @@ module ClassComposer
|
|
13
13
|
COMPOSER_ASSIGNED_ATTR_NAME = ->(name) { :"@__composer_#{name}_value_assigned__" }
|
14
14
|
COMPOSER_ASSIGNED_ARRAY_METHODS = ->(name) { :"@__composer_#{name}_array_methods_set__" }
|
15
15
|
|
16
|
-
def add_composer(name, allowed:,
|
17
|
-
|
18
|
-
|
16
|
+
def add_composer(name, allowed:, accessor: true, validator: ->(_) { true }, validation_error_klass: ::ClassComposer::ValidatorError, error_klass: ::ClassComposer::Error, **params)
|
17
|
+
default =
|
18
|
+
if params.has_key?(:default)
|
19
|
+
params[:default]
|
20
|
+
else
|
21
|
+
if allowed.is_a?(Array)
|
22
|
+
allowed << ClassComposer::DefaultObject
|
23
|
+
else
|
24
|
+
allowed = [allowed, ClassComposer::DefaultObject]
|
25
|
+
end
|
26
|
+
ClassComposer::DefaultObject
|
27
|
+
end
|
28
|
+
|
29
|
+
allowed.include?(ClassComposer::DefaultObject)
|
30
|
+
validate_proc = __composer_validator_proc__(validator: validator, allowed: allowed, name: name, error_klass: error_klass)
|
31
|
+
__composer_validate_options__!(name: name, validate_proc: validate_proc, default: default, validation_error_klass: validation_error_klass, error_klass: error_klass)
|
19
32
|
|
20
33
|
array_proc = __composer_array_proc__(name: name, validator: validator, allowed: allowed, params: params)
|
21
|
-
__composer_assignment__(name: name, params: params, validator: validate_proc, array_proc: array_proc)
|
34
|
+
__composer_assignment__(name: name, allowed: allowed, params: params, validator: validate_proc, array_proc: array_proc, validation_error_klass: validation_error_klass, error_klass: error_klass)
|
22
35
|
__composer_retrieval__(name: name, default: default, array_proc: array_proc)
|
23
36
|
end
|
24
37
|
|
25
|
-
def __composer_validate_options__!(name:, validate_proc:, default:)
|
38
|
+
def __composer_validate_options__!(name:, validate_proc:, default:, params: {}, validation_error_klass:, error_klass:)
|
26
39
|
unless validate_proc.(default)
|
27
|
-
raise
|
40
|
+
raise validation_error_klass, "Default value [#{default}] for #{self.class}.#{name} is not valid"
|
28
41
|
end
|
29
42
|
|
30
|
-
if
|
31
|
-
raise
|
43
|
+
if instance_methods.include?(name.to_sym)
|
44
|
+
raise error_klass, "[#{name}] is already defined. Ensure composer names are all uniq and do not class with class instance methods"
|
32
45
|
end
|
33
46
|
end
|
34
47
|
|
@@ -39,7 +52,7 @@ module ClassComposer
|
|
39
52
|
end
|
40
53
|
|
41
54
|
# create assignment method for the incoming name
|
42
|
-
def __composer_assignment__(name:, params:, validator:, array_proc:)
|
55
|
+
def __composer_assignment__(name:, params:, allowed:, validator:, array_proc:, validation_error_klass:, error_klass:)
|
43
56
|
define_method(:"#{name}=") do |value|
|
44
57
|
is_valid = validator.(value)
|
45
58
|
|
@@ -47,11 +60,15 @@ module ClassComposer
|
|
47
60
|
instance_variable_set(COMPOSER_ASSIGNED_ATTR_NAME.(name), true)
|
48
61
|
instance_variable_set(:"@#{name}", value)
|
49
62
|
else
|
50
|
-
|
51
|
-
message = ["#{self.class}.#{name} failed validation."]
|
63
|
+
message = ["#{self.class}.#{name} failed validation. #{name} is expected to be #{allowed}."]
|
52
64
|
|
53
65
|
message << (params[:invalid_message].is_a?(Proc) ? params[:invalid_message].(value) : params[:invalid_message].to_s)
|
54
|
-
|
66
|
+
if value.is_a?(Array)
|
67
|
+
# we assigned the array value...pop it from the array
|
68
|
+
# must be done after the message is created so that failing value can get passed appropriately
|
69
|
+
value.pop
|
70
|
+
end
|
71
|
+
raise validation_error_klass, message.compact.join(" ")
|
55
72
|
end
|
56
73
|
|
57
74
|
if value.is_a?(Array) && !value.instance_variable_get(COMPOSER_ASSIGNED_ARRAY_METHODS.(name))
|
@@ -79,14 +96,15 @@ module ClassComposer
|
|
79
96
|
end
|
80
97
|
default.instance_variable_set(COMPOSER_ASSIGNED_ARRAY_METHODS.(name), true)
|
81
98
|
end
|
82
|
-
|
99
|
+
|
100
|
+
default == ClassComposer::DefaultObject ? ClassComposer::DefaultObject.value : default
|
83
101
|
end
|
84
102
|
end
|
85
103
|
|
86
104
|
# create validator method for incoming name
|
87
|
-
def __composer_validator_proc__(validator:, allowed:, name:)
|
105
|
+
def __composer_validator_proc__(validator:, allowed:, name:, error_klass:)
|
88
106
|
if validator && !validator.is_a?(Proc)
|
89
|
-
raise
|
107
|
+
raise error_klass, "Expected validator to be a Proc. Received [#{validator.class}]"
|
90
108
|
end
|
91
109
|
|
92
110
|
# Proc will validate the entire attribute -- Full assignment must occur before validate is called
|
@@ -98,9 +116,11 @@ module ClassComposer
|
|
98
116
|
else
|
99
117
|
allowed == value.class
|
100
118
|
end
|
101
|
-
|
119
|
+
# order is important -- Do not run validator if it is the default object
|
120
|
+
# Default object will likely raise an error if there is a custom validator
|
121
|
+
(allowed.include?(ClassComposer::DefaultObject) && value == ClassComposer::DefaultObject) || (allow && validator.(value))
|
102
122
|
rescue StandardError => e
|
103
|
-
raise
|
123
|
+
raise error_klass, "#{e} occured during validation for value [#{value}]. Check custom validator for #{name}"
|
104
124
|
end
|
105
125
|
end
|
106
126
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: class_composer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Taylor
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry-byebug
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: 0.17.0
|
69
|
-
description:
|
69
|
+
description: Compose configurations for any class.
|
70
70
|
email:
|
71
71
|
- mattius.taylor@gmail.com
|
72
72
|
executables: []
|
@@ -90,7 +90,7 @@ files:
|
|
90
90
|
- class_composer.gemspec
|
91
91
|
- docker-compose.yml
|
92
92
|
- lib/class_composer.rb
|
93
|
-
- lib/class_composer/
|
93
|
+
- lib/class_composer/default_object.rb
|
94
94
|
- lib/class_composer/generator.rb
|
95
95
|
- lib/class_composer/version.rb
|
96
96
|
homepage: https://github.com/matt-taylor/class_composer
|
@@ -117,5 +117,6 @@ requirements: []
|
|
117
117
|
rubygems_version: 3.3.11
|
118
118
|
signing_key:
|
119
119
|
specification_version: 4
|
120
|
-
summary:
|
120
|
+
summary: Easily compose a class via inline code or passed in YAML config. Add instance
|
121
|
+
attributes with custom validations that will DRY up your code
|
121
122
|
test_files: []
|