u-attributes 2.4.0 → 2.8.0

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: 62c6ad9683f0824be6a32648028f25a97ba88a10c7fbdd0b9a2ccfbfe275961b
4
- data.tar.gz: d110a1b4220af76a7f14753cb1ca5067428f06db026466e095d54a22ee46f86f
3
+ metadata.gz: 75a44be0f1b71d41abdfd3e9c81f67527760a4a8f0202818fd7f04782680922b
4
+ data.tar.gz: bbb26faf04daffd2e92b18eb4e363d85c7677ed5ed3fb09bda485d8b34c8296d
5
5
  SHA512:
6
- metadata.gz: 24703dcd81aa785fe00b77a741434ad84600243f4bb132d2bbbce2b5a310d4f2b931c2b6ea41e311a2bcf07876296bd03d8e6c39a92905bc9a4f745fdb30a094
7
- data.tar.gz: 8172811ddaf44a3099e66f63cea5aed99d5c1a63a77944dc1e98c16fa0378603498fbdfe0c77f7220244c8d7b977801a75bd01facc875fb05f0bd1aed0171cc5
6
+ metadata.gz: 9e7da00d4786086edc9607533ef9c9632b10dd2200ddee29a46551d311c8e5ee8c5549d161f8ea6881c95c529abbdd4a2c48fd28635464606a7877b0d94a6488
7
+ data.tar.gz: aa42ef9c78b7f60514ef062b78d1b6f4eb3589ce1d39ab13d6459d2a164e3dcfa8b4f54660598df76afe4b42b8087cf9060f57398609f59508952bfd49c0cda1
@@ -0,0 +1,27 @@
1
+
2
+ name: build
3
+ on: [push]
4
+ jobs:
5
+ test:
6
+ runs-on: ubuntu-latest
7
+ strategy:
8
+ matrix:
9
+ ruby: [2.2, 2.3, 2.4, 2.5, 2.6, 3.0]
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: ${{ matrix.ruby }}
15
+ bundler-cache: true
16
+ - name: Test and generate coverage
17
+ run: bin/test
18
+ - name: Format coverage
19
+ if: ${{ matrix.ruby >= 3 }}
20
+ run: bin/prepare_coverage
21
+ - uses: paambaati/codeclimate-action@v2.7.5
22
+ if: ${{ matrix.ruby >= 3 }}
23
+ env:
24
+ CC_TEST_REPORTER_ID: 9015766fe58e0abd5ab58050ddfc67708e0b11430b3d3999ac17139ffcb4d3e7
25
+ with:
26
+ debug: true
27
+ coverageLocations: coverage/.resultset.json:simplecov
@@ -0,0 +1,8 @@
1
+ {
2
+ "cSpell.enabled": true,
3
+ "cSpell.ignoreWords": [
4
+ "paambaati",
5
+ "resultset",
6
+ "simplecov"
7
+ ]
8
+ }
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gem 'u-case', '~> 4.0'
3
+ gem 'u-case', '~> 4.5', '>= 4.5.1'
4
4
 
5
- activemodel_version = ENV.fetch('ACTIVEMODEL_VERSION', '6.1')
5
+ activemodel_version = ENV['ACTIVEMODEL_VERSION']
6
6
 
7
7
  activemodel = case activemodel_version
8
8
  when '3.2' then '3.2.22'
@@ -11,26 +11,31 @@ activemodel = case activemodel_version
11
11
  when '4.2' then '4.2.11'
12
12
  when '5.0' then '5.0.7'
13
13
  when '5.1' then '5.1.7'
14
- when '5.2' then '5.2.3'
15
- when '6.0' then '6.0.0'
14
+ when '5.2' then '5.2.4'
15
+ when '6.0' then '6.0.3.4'
16
+ when '6.1' then '6.1.2'
16
17
  end
17
18
 
18
- if activemodel_version < '6.1'
19
- gem 'activemodel', activemodel, require: false
20
- gem 'activesupport', activemodel, require: false
21
- end
22
-
23
19
  simplecov_version =
24
20
  case RUBY_VERSION
25
- when /\A2.[23]/ then '~> 0.17.1'
21
+ when /\A2.[23]/ then '0.17.1'
26
22
  when /\A2.4/ then '~> 0.18.5'
27
- else '~> 0.19'
23
+ else '~> 0.21.2'
28
24
  end
29
25
 
30
26
  group :test do
31
- gem 'minitest', activemodel_version < '4.1' ? '~> 4.2' : '~> 5.0'
27
+ if activemodel_version
28
+ gem 'activesupport', activemodel, require: false
29
+ gem 'activemodel', activemodel, require: false
30
+ gem 'minitest', activemodel_version < '4.1' ? '~> 4.2' : '~> 5.0'
31
+ else
32
+ gem 'minitest', '~> 5.0'
33
+ end
34
+
32
35
  gem 'simplecov', simplecov_version, require: false
33
36
  end
34
37
 
38
+ gem 'rake', '~> 13.0'
39
+
35
40
  # Specify your gem's dependencies in u-attributes.gemspec
36
41
  gemspec
data/README.md CHANGED
@@ -1,19 +1,19 @@
1
1
  <p align="center">
2
2
  <img src="./assets/u-attributes_logo_v1.png" alt='Create "immutable" objects. No setters, just getters!'>
3
3
 
4
- <p align="center"><i>Create "immutable" objects. No setters, just getters!</i></p>
4
+ <p align="center"><i>Create "immutable" objects with no setters, just getters.</i></p>
5
5
  <br>
6
6
  </p>
7
7
 
8
8
  <p align="center">
9
- <img src="https://img.shields.io/badge/ruby-2.2+-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
9
+ <img src="https://img.shields.io/badge/ruby->%3D%202.2.0-ruby.svg?colorA=99004d&colorB=cc0066" alt="Ruby">
10
10
 
11
11
  <a href="https://rubygems.org/gems/u-attributes">
12
12
  <img alt="Gem" src="https://img.shields.io/gem/v/u-attributes.svg?style=flat-square">
13
13
  </a>
14
14
 
15
- <a href="https://travis-ci.com/serradura/u-attributes">
16
- <img alt="Build Status" src="https://travis-ci.com/serradura/u-attributes.svg?branch=main">
15
+ <a href="https://github.com/serradura/u-attributes/actions/workflows/ci.yml">
16
+ <img alt="Build Status" src="https://github.com/serradura/u-attributes/actions/workflows/ci.yml/badge.svg">
17
17
  </a>
18
18
 
19
19
  <a href="https://codeclimate.com/github/serradura/u-attributes/maintainability">
@@ -25,8 +25,16 @@
25
25
  </a>
26
26
  </p>
27
27
 
28
- This gem allows you to define "immutable" objects, and your objects will have only getters and no setters.
29
- So, if you change [[1](#with_attribute)] [[2](#with_attributes)] some object attribute, you will have a new object instance. That is, you transform the object instead of modifying it.
28
+ This gem allows you to define "immutable" objects, when using it your objects will only have getters and no setters.
29
+ So, if you change [[1](#with_attribute)] [[2](#with_attributes)] an attribute of the object, you’ll have a new object instance. That is, you transform the object instead of modifying it.
30
+
31
+ ## Documentation <!-- omit in toc -->
32
+
33
+ Version | Documentation
34
+ ---------- | -------------
35
+ unreleased | https://github.com/serradura/u-case/blob/main/README.md
36
+ 2.8.0 | https://github.com/serradura/u-case/blob/v2.x/README.md
37
+ 1.2.0 | https://github.com/serradura/u-case/blob/v1.x/README.md
30
38
 
31
39
  # Table of contents <!-- omit in toc -->
32
40
  - [Installation](#installation)
@@ -85,7 +93,8 @@ gem 'u-attributes'
85
93
 
86
94
  | u-attributes | branch | ruby | activemodel |
87
95
  | -------------- | ------- | -------- | ------------- |
88
- | 2.4.0 | main | >= 2.2.0 | >= 3.2, < 6.1 |
96
+ | unreleased | main | >= 2.2.0 | >= 3.2, < 7 |
97
+ | 2.8.0 | v2.x | >= 2.2.0 | >= 3.2, < 7 |
89
98
  | 1.2.0 | v1.x | >= 2.2.0 | >= 3.2, < 6.1 |
90
99
 
91
100
  > **Note**: The activemodel is an optional dependency, this module [can be enabled](#activemodelvalidation-extension) to validate the attributes.
@@ -618,7 +627,7 @@ The method `Micro::Attributes.with()` will raise an exception if no arguments/fe
618
627
 
619
628
  ```ruby
620
629
  class Job
621
- include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: :activemodel_validations, :diff, :initialize, :keys_as_symbol)
630
+ include Micro::Attributes.with() # ArgumentError (Invalid feature name! Available options: :accept, :activemodel_validations, :diff, :initialize, :keys_as_symbol)
622
631
  end
623
632
  ```
624
633
 
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ # Borrowed from https://gist.github.com/qortex/7e7c49f3731391a91ee898336183acef
4
+
5
+ # Temporary hack to get CodeClimate to work with SimpleCov 0.18 JSON format until issue is fixed
6
+ # upstream: https://github.com/codeclimate/test-reporter/issues/413
7
+
8
+ require "json"
9
+
10
+ filename = "coverage/.resultset.json"
11
+ contents = JSON.parse(File.read(filename))
12
+
13
+ def remove_lines_key(obj)
14
+ case obj
15
+ when Hash
16
+ obj.transform_values do |val|
17
+ val.is_a?(Hash) && val.key?("lines") ? val["lines"] : remove_lines_key(val)
18
+ end
19
+ else
20
+ obj
21
+ end
22
+ end
23
+
24
+ # overwrite
25
+ File.write(filename, JSON.generate(remove_lines_key(contents)))
26
+
27
+ puts Dir['coverage/.*.json']
data/bin/test ADDED
@@ -0,0 +1,48 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ RUBY_V=$(ruby -v)
6
+
7
+ function reset_gemfile_and_test {
8
+ rm Gemfile.lock
9
+
10
+ eval "$1 bundle update"
11
+ eval "$1 bundle exec rake test"
12
+ }
13
+
14
+ function test_with_activemodel {
15
+ reset_gemfile_and_test "ACTIVEMODEL_VERSION=$1"
16
+ }
17
+
18
+ RUBY_2_2345="ruby 2.[2345]."
19
+ RUBY_2_234="ruby 2.[234]."
20
+ RUBY_2_567="ruby 2.[567]."
21
+ RUBY_2_2="ruby 2.2."
22
+ RUBY_3_X="ruby 3.0."
23
+
24
+ if [[ $RUBY_V =~ $RUBY_2_2345 ]]; then
25
+ if [[ $RUBY_V =~ $RUBY_2_234 ]]; then
26
+ reset_gemfile_and_test
27
+ fi
28
+
29
+ if [[ $RUBY_V =~ $RUBY_2_2 ]]; then
30
+ test_with_activemodel "3.2"
31
+ fi
32
+
33
+ test_with_activemodel "4.0"
34
+ test_with_activemodel "4.1"
35
+ test_with_activemodel "4.2"
36
+ test_with_activemodel "5.0"
37
+ test_with_activemodel "5.1"
38
+ test_with_activemodel "5.2"
39
+ fi
40
+
41
+ if [[ $RUBY_V =~ $RUBY_2_567 ]] || [[ $RUBY_V =~ $RUBY_3_X ]]; then
42
+ gem install bundler -v ">= 2" --no-doc
43
+
44
+ reset_gemfile_and_test
45
+
46
+ test_with_activemodel "6.0"
47
+ test_with_activemodel "6.1"
48
+ fi
@@ -12,7 +12,7 @@ module Micro::Attributes
12
12
  def initialize(from:, to:)
13
13
  @from_class = from.class
14
14
 
15
- @from, @to = from, Kind::Of.(@from_class, to)
15
+ @from, @to = from, Kind.of(@from_class, to)
16
16
 
17
17
  @from_key, @to_key =
18
18
  @from_class.attributes_access == :symbol ? FROM_TO_SYM : FROM_TO_STR
@@ -35,17 +35,17 @@ module Micro::Attributes
35
35
 
36
36
  raise ArgumentError, FROM_TO_ERROR
37
37
  elsif from.nil? && to.nil?
38
- differences.has_key?(key_access(name))
38
+ differences.has_key?(key_transform(name))
39
39
  else
40
- result = @differences[key_access(name)]
40
+ result = @differences[key_transform(name)]
41
41
  result ? result[@from_key] == from && result[@to_key] == to : false
42
42
  end
43
43
  end
44
44
 
45
45
  private
46
46
 
47
- def key_access(key)
48
- @from_class.__attribute_key__(key)
47
+ def key_transform(key)
48
+ @from_class.__attribute_key_transform__(key)
49
49
  end
50
50
 
51
51
  def diff(from_attributes, to_attributes)
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Attributes
4
+ module Features
5
+ module Accept
6
+
7
+ module Strict
8
+ ATTRIBUTES_REJECTED = "One or more attributes were rejected. Errors:\n".freeze
9
+
10
+ def __call_after_attributes_assign
11
+ return unless attributes_errors?
12
+
13
+ __raise_error_if_found_attributes_errors
14
+ end
15
+
16
+ def __raise_error_if_found_attributes_errors
17
+ raise ArgumentError, [
18
+ ATTRIBUTES_REJECTED,
19
+ attributes_errors.map { |key, msg| "* #{key.inspect} #{msg}" }.join("\n")
20
+ ].join
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Micro::Attributes
4
+ module Features
5
+ module Accept
6
+ def attributes_errors
7
+ @__attributes_errors
8
+ end
9
+
10
+ def attributes_errors?
11
+ !@__attributes_errors.empty?
12
+ end
13
+
14
+ def rejected_attributes
15
+ @__rejected_attributes ||= attributes_errors.keys
16
+ end
17
+
18
+ def accepted_attributes
19
+ @__accepted_attributes ||= defined_attributes - rejected_attributes
20
+ end
21
+
22
+ def rejected_attributes?
23
+ attributes_errors?
24
+ end
25
+
26
+ def accepted_attributes?
27
+ !rejected_attributes?
28
+ end
29
+
30
+ private
31
+
32
+ def __call_before_attributes_assign
33
+ @__attributes_errors = {}
34
+ end
35
+
36
+ KeepProc = -> validation_data { validation_data[0] == :accept && validation_data[1] == Proc }
37
+
38
+ def __attribute_assign(key, init_hash, attribute_data)
39
+ validation_data = attribute_data[1]
40
+
41
+ value_to_assign = FetchValueToAssign.(init_hash, init_hash[key], attribute_data, KeepProc.(validation_data))
42
+
43
+ value = __attributes[key] = instance_variable_set("@#{key}", value_to_assign)
44
+
45
+ __attribute_accept_or_reject(key, value, validation_data) if !validation_data.empty?
46
+ end
47
+
48
+ def __attribute_accept_or_reject(key, value, validation_data)
49
+ context = Context.with(key, value, validation_data)
50
+
51
+ error_msg = context.rejection_message(Validate.call(context))
52
+
53
+ @__attributes_errors[key] = error_msg if error_msg
54
+ end
55
+
56
+ Context = Struct.new(:key, :value, :validation, :expected, :allow_nil, :rejection) do
57
+ def self.with(key, value, data)
58
+ new(key, value, data[0], data[1], data[2], data[3])
59
+ end
60
+
61
+ def allow_nil?
62
+ allow_nil && value.nil?
63
+ end
64
+
65
+ def accept?
66
+ validation == :accept
67
+ end
68
+
69
+ def rejection_message(default_msg)
70
+ return unless default_msg
71
+
72
+ return default_msg unless rejection || expected.respond_to?(:rejection_message)
73
+
74
+ rejection_msg = rejection || expected.rejection_message
75
+
76
+ return rejection_msg unless rejection_msg.is_a?(Proc)
77
+
78
+ rejection_msg.arity == 0 ? rejection_msg.call : rejection_msg.call(key)
79
+ end
80
+ end
81
+
82
+ module Validate
83
+ module Callable
84
+ MESSAGE = 'is invalid'.freeze
85
+
86
+ def self.call?(exp); exp.respond_to?(:call); end
87
+ def self.call(exp, val); exp.call(val); end
88
+ def self.accept_failed(_exp); MESSAGE; end
89
+ def self.reject_failed(_exp); MESSAGE; end
90
+ end
91
+
92
+ module KindOf
93
+ def self.call?(exp); exp.is_a?(Class) || exp.is_a?(Module); end
94
+ def self.call(exp, val); val.kind_of?(exp); end
95
+ def self.accept_failed(exp); "expected to be a kind of #{exp}"; end
96
+ def self.reject_failed(exp); "expected to not be a kind of #{exp}"; end
97
+ end
98
+
99
+ module Predicate
100
+ QUESTION_MARK = '?'.freeze
101
+
102
+ def self.call?(exp); exp.is_a?(Symbol) && exp.to_s.end_with?(QUESTION_MARK); end
103
+ def self.call(exp, val); val.public_send(exp); end
104
+ def self.accept_failed(exp); "expected to be #{exp}"; end
105
+ def self.reject_failed(exp); "expected to not be #{exp}"; end
106
+ end
107
+
108
+ def self.with(expected)
109
+ return Callable if Callable.call?(expected)
110
+ return KindOf if KindOf.call?(expected)
111
+ return Predicate if Predicate.call?(expected)
112
+ end
113
+
114
+ def self.call(context)
115
+ return if context.allow_nil?
116
+
117
+ validate = self.with(expected = context.expected)
118
+
119
+ return unless validate
120
+
121
+ truthy = validate.call(expected, context.value)
122
+
123
+ return truthy ? nil : validate.accept_failed(expected) if context.accept?
124
+
125
+ validate.reject_failed(expected) if truthy
126
+ end
127
+ end
128
+
129
+ private_constant :KeepProc, :Context, :Validate
130
+ end
131
+ end
132
+ end
@@ -3,13 +3,45 @@
3
3
  module Micro::Attributes
4
4
  module Features
5
5
  module ActiveModelValidations
6
- def self.included(base)
7
- begin
8
- require 'active_model'
6
+ module Standard
7
+ private def __call_after_attributes_assign
8
+ run_validations!
9
+ end
10
+ end
9
11
 
10
- base.send(:include, ::ActiveModel::Validations)
11
- base.extend(ClassMethods)
12
- rescue LoadError
12
+ module CheckActivemodelValidationErrors
13
+ private def __check_activemodel_validation_errors
14
+ return if errors.blank?
15
+
16
+ errors_hash = errors.to_hash
17
+
18
+ defined_attributes.each do |key|
19
+ value = Utils::Hashes.assoc(errors_hash, key)
20
+
21
+ @__attributes_errors[key] = value.join(', ') if value.present?
22
+ end
23
+ end
24
+ end
25
+
26
+ module WithAccept
27
+ include CheckActivemodelValidationErrors
28
+
29
+ private def __call_after_attributes_assign
30
+ run_validations! unless attributes_errors?
31
+
32
+ __check_activemodel_validation_errors
33
+ end
34
+ end
35
+
36
+ module WithAcceptStrict
37
+ include CheckActivemodelValidationErrors
38
+
39
+ private def __call_after_attributes_assign
40
+ __raise_error_if_found_attributes_errors if attributes_errors?
41
+
42
+ run_validations!
43
+
44
+ __check_activemodel_validation_errors
13
45
  end
14
46
  end
15
47
 
@@ -22,11 +54,23 @@ module Micro::Attributes
22
54
  end
23
55
  end
24
56
 
25
- private
57
+ def self.included(base)
58
+ begin
59
+ require 'active_model'
60
+
61
+ base.send(:include, ::ActiveModel::Validations)
62
+ base.extend(ClassMethods)
26
63
 
27
- def __call_after_micro_attribute
28
- run_validations! if respond_to?(:run_validations!, true)
64
+ case
65
+ when base <= Features::Accept::Strict then base.send(:include, WithAcceptStrict)
66
+ when base <= Features::Accept then base.send(:include, WithAccept)
67
+ else base.send(:include, Standard)
68
+ end
69
+ rescue LoadError
29
70
  end
71
+ end
72
+
73
+ private_constant :Standard, :CheckActivemodelValidationErrors, :WithAccept, :WithAcceptStrict
30
74
  end
31
75
  end
32
76
  end