class_composer 0.0.1 → 1.0.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: a1bb350ed5d28637c5d2cf2574450eb66459c14c06a49e6f427eaece430df73f
4
- data.tar.gz: db684a27bbe7b948380538952c1a9f6a348c6c2c96c1e7774915b92709b0f372
3
+ metadata.gz: 7d53fe12431ff8f1dd4c42d7282d11a64637a0c3ab573e24a7d83443a8a9cac2
4
+ data.tar.gz: 0376e66b2fef6bc976289dd1f4e4517830dd0a63c0e12519730efc9786865c9f
5
5
  SHA512:
6
- metadata.gz: 3a316374ba96891a3b1e202893bb79d125db16126636a981b7a19f5aa3caad8b34756f6b650bbc4632996e2de49e8ceb2399b40773213145570d7804fc77a4bd
7
- data.tar.gz: 0e04d577ad1984244d0a89be64c407f456d53641486ec06531d7cf390ce8439029dc8cdea7ee42acadb123d8ee1c5e7888e061a1524cee3b2b52c842fa1282d8
6
+ metadata.gz: b08e706f2f55865e6d4fab779896450dab524b946412ee8e8be8a673b205a8f3ded0a438db6dbce53d8ebc06c6a88a2dc6cba5a4da3bd5e22a599a205ea9cc0f
7
+ data.tar.gz: bfa9491b16358fc59b221bd5ac847b861c52a6a5793dca4fc59c81a8ceef3e12c1d3148bd8679833e4c5a643e5738788a9647a1619f9aadc2171b9d497bd0f34
data/CHANGELOG.md CHANGED
@@ -1,26 +1,15 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
3
+ ### Versioning
4
4
 
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
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
- ## [Unreleased]
7
+ **Minor:** x.x - Minor changes or features added which are backwords compatible
9
8
 
10
- ## [0.0.1] - 2022-06-18
9
+ **Patch:** x.x.x - Patch Updates
11
10
 
12
- ### Added
13
- - New feature 1
14
- - New feature 2
11
+ # Release Notes
15
12
 
16
- ### Changed
17
- - Changed feature 1
18
- - Changed feature 2
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- class_composer (0.0.1)
4
+ class_composer (0.1.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,14 +1,11 @@
1
1
  # ClassComposer
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be
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
- TODO: Write usage instructions here
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
 
@@ -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 = "Describe the gem here"
12
- spec.description = "Describe the gem here"
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/version"
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:, default: nil, accessor: true, validator: ->(_) { true }, **params)
17
- validate_proc = __composer_validator_proc__(validator: validator, allowed: allowed, name: name)
18
- __composer_validate_options__!(name: name, validate_proc: validate_proc, default: default)
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 ClassComposer::ValidatorError, "Default value [#{default}] for #{self.class}.#{name} is not valid"
40
+ raise validation_error_klass, "Default value [#{default}] for #{self.class}.#{name} is not valid"
28
41
  end
29
42
 
30
- if self.class.instance_methods.include?(name.to_sym)
31
- raise ClassComposer::Error, "#{name} is already defined. Ensure composer names are all uniq and do not class with class instance methods"
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
- value.pop
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
- raise ClassComposer::ValidatorError, message.compact.join(" ")
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
- default
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 ClassComposer::Error, "Expected validator to be a Proc. Received [#{validator.class}]"
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
- allow && validator.(value)
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 ClassComposer::Error, "#{e} occured during validation for value [#{value}]. Check custom validator for #{name}"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ClassComposer
4
- VERSION = "0.0.1"
4
+ VERSION = "1.0.2"
5
5
  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: 0.0.1
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-06-24 00:00:00.000000000 Z
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: Describe the gem here
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/array.rb
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: Describe the gem here
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: []
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ClassComposer
4
- class Array
5
- def initialize(validator)
6
- @validator = validator
7
- super
8
- end
9
-
10
- def <<
11
- value = super
12
-
13
-
14
-
15
- value
16
- end
17
- end
18
- end