operations 0.7.1 → 0.7.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: 41842fbe905d259c1c39bd2922caa0dc50583cdfceac0c0b78ce7e4e8e1d26f4
4
- data.tar.gz: 37e703dca9a9db80167e2750e77af20e59494a3fb1c0f208720e08ae2bb83a8e
3
+ metadata.gz: affb68a66ecbc1ee0e687ab13efb202037d10054adb114e684e775224957b65e
4
+ data.tar.gz: 255180d0be3704a73c0bf6e148658df8b662e33a4508dff3ae3fee819e73caba
5
5
  SHA512:
6
- metadata.gz: 33ebaee09aff4d2cd32742566e1c3959c5f43558b6e3c57760936a31f8431af2806a8b6b8807da10588fbf2916de5e34857cc9ce5378b93d70099dd09b049d78
7
- data.tar.gz: 1f733f0567064105528b480d47f766aa6acd9b2dd2766d011e11edeb542f3eea6233f2e30cb810d10f4e9412ab9548a186643072dd80414cf07b9aecc7189570
6
+ metadata.gz: 9f639386ccb6d003909737a4606b44f21cf49dc90b36fe0c854ea1e8c824049b2de7a70c480b44b2f4713558f21417c755361b6fccad4ed2f2af1068bc7cd135
7
+ data.tar.gz: a24d378627511c1198728a9aebe38c7f39cb057756e957906df29bf059804eef94d1188146b845842be42d94c559ebccd84b3d4d6a613b95a2a31d427771fe09
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-05-05 08:51:48 UTC using RuboCop version 1.59.0.
3
+ # on 2024-08-07 02:24:58 UTC using RuboCop version 1.65.0.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -22,14 +22,14 @@ Metrics/AbcSize:
22
22
  # Offense count: 1
23
23
  # Configuration parameters: CountComments, CountAsOne.
24
24
  Metrics/ClassLength:
25
- Max: 145
25
+ Max: 149
26
26
 
27
27
  # Offense count: 1
28
28
  # Configuration parameters: CountComments, CountAsOne.
29
29
  Metrics/ModuleLength:
30
- Max: 142
30
+ Max: 141
31
31
 
32
- # Offense count: 2
32
+ # Offense count: 3
33
33
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
34
34
  Metrics/ParameterLists:
35
35
  Max: 7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.7.2](https://github.com/BookingSync/operations/tree/v0.7.2)
4
+
5
+ ### Added
6
+
7
+ - Allow referencing and arbitrary model attrbiute from form object attribute with `model_name: "Post#title"` [\#50](https://github.com/BookingSync/operations/pull/50) ([pyromaniac](https://github.com/pyromaniac))
8
+ - Allow passing multiple `hydrators:` to Operations::Form [\#49](https://github.com/BookingSync/operations/pull/49) ([pyromaniac](https://github.com/pyromaniac))
9
+
10
+ ### Improvements
11
+
12
+ - Change default form hydration behavior - it is now deep merging params after hydration, so no need to do it in the hydrator. Controlled with `hydration_merge_params:` option. [\#49](https://github.com/BookingSync/operations/pull/49) ([pyromaniac](https://github.com/pyromaniac))
13
+
3
14
  ## [0.7.1](https://github.com/BookingSync/operations/tree/v0.7.1)
4
15
 
5
16
  ### Added
data/README.md CHANGED
@@ -312,7 +312,7 @@ class ActiveRecordRepository
312
312
  include Dry::Monads[:result]
313
313
  extend Dry::Initializer
314
314
 
315
- param :model, type: Types.Instance(Class).constrained(lt: ActiveRecord::Base)
315
+ param :model, type: Types::Class.constrained(lt: ActiveRecord::Base)
316
316
 
317
317
  def create(**attributes)
318
318
  record = model.new(**attributes)
@@ -904,7 +904,7 @@ class Post::Update
904
904
  def self.default_form
905
905
  @default_form ||= Operations::Form.new(
906
906
  default,
907
- hydrator: Post::Update::Hydrator.new
907
+ hydrators: [Post::Update::Hydrator.new]
908
908
  )
909
909
  end
910
910
  end
@@ -912,9 +912,7 @@ end
912
912
  class Post::Update::Hydrator
913
913
  def call(form_class, params, post:, **_context)
914
914
  value_attributes = form_class.attributes.keys - %i[post_id]
915
- data = value_attributes.index_with { |name| post.public_send(name) }
916
-
917
- data.merge!(params)
915
+ value_attributes.index_with { |name| post.public_send(name) }
918
916
  end
919
917
  end
920
918
  ```
@@ -929,14 +927,20 @@ class Post::Update
929
927
  @default_form ||= Operations::Form.new(
930
928
  default,
931
929
  model_map: Post::Update::ModelMap.new,
932
- hydrator: Post::Update::Hydrator.new
930
+ hydrators: [Post::Update::Hydrator.new]
933
931
  )
934
932
  end
935
933
  end
936
934
 
937
935
  class Post::Update::ModelMap
938
- def call(_path)
939
- Post
936
+ MAPPING = {
937
+ %w[published_at] => Post, # a model can be passed but beware of circular dependencies, better use strings
938
+ %w[title] => "Post", # or a model name - safer option
939
+ %w[content] => "Post#body" # referencing different attribute is possible, useful for naming migration or translations
940
+ }.freeze
941
+
942
+ def call(path)
943
+ MAPPING[path] # returns the mapping for a single path
940
944
  end
941
945
  end
942
946
  ```
data/UPGRADING_FORMS.md CHANGED
@@ -31,7 +31,7 @@ class Post::Update
31
31
  def self.default_form
32
32
  @default_form ||= Operations::Form.new(
33
33
  default,
34
- hydrator: Post::Update::Hydrator.new,
34
+ hydrators: [Post::Update::Hydrator.new],
35
35
  model_map: Post::Update::ModelMap.new,
36
36
  params_transformations: [
37
37
  ParamsMap.new(id: :post_id)
@@ -197,6 +197,11 @@ class Operations::Command
197
197
  result_policies = policies_sum - [Undefined] unless policies_sum == [Undefined, Undefined]
198
198
  options[:policies] = result_policies if result_policies
199
199
 
200
+ if after.present?
201
+ ActiveSupport::Deprecation.new.warn("Operations::Command `after:` option is deprecated and will be " \
202
+ "removed in 1.0.0. Please use `on_success:` instead")
203
+ end
204
+
200
205
  preconditions.push(precondition) if precondition.present?
201
206
  super(operation, preconditions: preconditions, on_success: after, **options)
202
207
  end
@@ -5,39 +5,35 @@
5
5
  # legacy UI.
6
6
  class Operations::Form::Attribute
7
7
  extend Dry::Initializer
8
- include Dry::Equalizer(:name, :collection, :model_name, :form)
9
- include Operations::Inspect.new(:name, :collection, :model_name, :form)
8
+ include Dry::Equalizer(:name, :collection, :model_class, :model_attribute, :form)
9
+ include Operations::Inspect.new(:name, :collection, :model_class, :model_attribute, :form)
10
10
 
11
11
  param :name, type: Operations::Types::Coercible::Symbol
12
12
  option :collection, type: Operations::Types::Bool, default: proc { false }
13
- option :model_name,
14
- type: (Operations::Types::String | Operations::Types.Instance(Class).constrained(lt: ActiveRecord::Base)).optional,
15
- default: proc {}
13
+ option :model_name, type: (Operations::Types::String | Operations::Types::Class).optional, default: proc {}
16
14
  option :form, type: Operations::Types::Class.optional, default: proc {}
17
15
 
18
- def model_type
19
- @model_type ||= owning_model.type_for_attribute(string_name) if model_name
20
- end
16
+ def model_class
17
+ return @model_class if defined?(@model_class)
21
18
 
22
- def model_human_name(options = {})
23
- owning_model.human_attribute_name(string_name, options) if model_name
19
+ @model_class = model_name.is_a?(String) ? model_name.split("#").first.constantize : model_name
24
20
  end
25
21
 
26
- def model_validators
27
- @model_validators ||= model_name ? owning_model.validators_on(string_name) : []
28
- end
22
+ def model_attribute
23
+ return @model_attribute if defined?(@model_attribute)
29
24
 
30
- def model_localized_attr_name(locale)
31
- owning_model.localized_attr_name_for(string_name, locale) if model_name
25
+ @model_attribute = model_class && (model_name.to_s.split("#").second.presence || name.to_s)
32
26
  end
33
27
 
34
- private
28
+ def model_type
29
+ model_class.type_for_attribute(model_attribute) if model_name
30
+ end
35
31
 
36
- def owning_model
37
- @owning_model ||= model_name.is_a?(String) ? model_name.constantize : model_name
32
+ def model_human_name(options = {})
33
+ model_class.human_attribute_name(model_attribute, options) if model_name
38
34
  end
39
35
 
40
- def string_name
41
- @string_name ||= name.to_s
36
+ def model_validators
37
+ model_name ? model_class.validators_on(model_attribute) : []
42
38
  end
43
39
  end
@@ -50,7 +50,13 @@ class Operations::Form::Base
50
50
  base.class_attribute :persisted, instance_accessor: false, default: nil
51
51
 
52
52
  base.define_method :initialize do |*args, **kwargs|
53
- args.empty? && kwargs.present? ? super(kwargs, **{}) : super(*args, **kwargs)
53
+ if args.empty?
54
+ # Initializing Operations::Form::Base instance
55
+ super(kwargs, **{})
56
+ else
57
+ # Initializing Operations::Form instance as form object (deprecated)
58
+ super(*args, **kwargs)
59
+ end
54
60
  end
55
61
  end
56
62
 
@@ -83,12 +89,13 @@ class Operations::Form::Base
83
89
 
84
90
  # :nodoc:
85
91
  module InstanceMethods
86
- def type_for_attribute(name)
87
- self.class.attributes[name.to_sym].model_type
92
+ # Copied from globalize-accessors, should be deprecated and removed as it is not a core method
93
+ def localized_attr_name_for(attr_name, locale)
94
+ "#{attr_name}_#{locale.to_s.underscore}"
88
95
  end
89
96
 
90
- def localized_attr_name_for(name, locale)
91
- self.class.attributes[name.to_sym].model_localized_attr_name(locale)
97
+ def type_for_attribute(name)
98
+ self.class.attributes[name.to_sym].model_type
92
99
  end
93
100
 
94
101
  def has_attribute?(name) # rubocop:disable Naming/PredicateName
@@ -21,8 +21,10 @@
21
21
  #
22
22
  class Operations::Form
23
23
  include Dry::Core::Constants
24
- include Dry::Equalizer(:command, :model_map, :persisted, :params_transformations, :hydrator, :form_class)
25
- include Operations::Inspect.new(:model_name, :model_map, :persisted, :params_transformations, :hydrator, :form_class)
24
+ include Dry::Equalizer(:command, :model_map, :persisted,
25
+ :params_transformations, :hydrators, :hydration_merge_params, :form_class)
26
+ include Operations::Inspect.new(:model_name, :model_map, :persisted,
27
+ :params_transformations, :hydrators, :hydration_merge_params, :form_class)
26
28
 
27
29
  # We need to make deprecated inheritance from Operations::Form act exactly the
28
30
  # same way as from Operations::Form::Base. In order to do this, we are encapsulating all the
@@ -51,10 +53,17 @@ class Operations::Form
51
53
  option :persisted, type: Operations::Types::Bool, default: proc { true }
52
54
  option :params_transformations, type: Operations::Types::Coercible::Array.of(Operations::Types.Interface(:call)),
53
55
  default: proc { [] }
54
- option :hydrator, type: Operations::Types.Interface(:call).optional, default: proc {}
56
+ option :hydrators, type: Operations::Types::Array.of(Operations::Types.Interface(:call)), default: proc { [] }
57
+ option :hydration_merge_params, type: Operations::Types::Bool, default: proc { true }
55
58
  option :base_class, type: Operations::Types::Class, default: proc { ::Operations::Form::Base }
56
59
  end)
57
60
 
61
+ def initialize(command, hydrator: nil, hydrators: [], **options)
62
+ hydrators.push(hydrator) if hydrator.present?
63
+
64
+ super(command, hydrators: hydrators, **options)
65
+ end
66
+
58
67
  def build(params = EMPTY_HASH, **context)
59
68
  instantiate_form(command.callable(transform_params(params, **context), **context))
60
69
  end
@@ -81,12 +90,20 @@ class Operations::Form
81
90
 
82
91
  def instantiate_form(operation_result)
83
92
  form_class.new(
84
- hydrator&.call(form_class, operation_result.params, **operation_result.context) || {},
93
+ hydrate_params(form_class, operation_result.params, **operation_result.context),
85
94
  messages: operation_result.errors.to_h,
86
95
  operation_result: operation_result
87
96
  )
88
97
  end
89
98
 
99
+ def hydrate_params(form_class, params, **context)
100
+ hydrated_params = hydrators.inject({}) do |value, hydrator|
101
+ value.merge(hydrator.call(form_class, params, **context).deep_symbolize_keys)
102
+ end
103
+ hydrated_params.deep_merge!(params) if hydration_merge_params
104
+ hydrated_params
105
+ end
106
+
90
107
  def key_map
91
108
  @key_map ||= command.contract.schema.key_map
92
109
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Operations
4
- VERSION = "0.7.1"
4
+ VERSION = "0.7.2"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: operations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arkadiy Zabazhanov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-29 00:00:00.000000000 Z
11
+ date: 2024-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appraisal