typed_operation 1.0.0.beta1 → 1.0.0.beta2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 464b0e645a604caaee0ab2b83f21b56cb0ca7a6b6b69e3475dcce17366ed2c80
4
- data.tar.gz: c59c8522a9c8c04be5a589649596b85bd42cb8f4faeae4073230fd42470556ce
3
+ metadata.gz: 501477c614c107723d1c915099dd06209336708ba5e9672c26622d02a9cd4782
4
+ data.tar.gz: b5ab846baf478e3cce3fd8ea1752c04ee708c9a7f84d2be6d47d4c9a94505ac5
5
5
  SHA512:
6
- metadata.gz: 7c42242a9df90fd0751b3e5fcd77a6b71ded4e9bc5cfc7725082e2d1ff200643dce87c10e7f80d2ae2eececb2d00231083396ed1d2aaf10a4fb88e1eaa287aa3
7
- data.tar.gz: bfad02c10284d3895956b3390ac6529f1b815286f19e2cb5644ed5b687d01e2248f664cc166925d9db4a521ebb20956143fb9a7c98592c193915cbb812ffd90b
6
+ metadata.gz: c3c399b74903b5b9e40ab7838c7d20bafa68969d1200bf2afb11457e41437ae8fa823e7afc14fabe71cd290cd798823f97a0918c4a12d50f6a8768ac1a77ea8e
7
+ data.tar.gz: b1ce5f67d1b87ef907f00904288b25edd678a2afab0f68a5f2c5804831f5d05df0fad134a4c28a4893d3867f61c671d548bf687f37b822ff32754355845d9abd
data/README.md CHANGED
@@ -10,7 +10,7 @@ Type of result of the operation is up to you, eg you could use [`literal` monads
10
10
 
11
11
  ```ruby
12
12
  gem "literal", github: "joeldrapper/literal", branch: "main"
13
- gem "typed_operation", "~> 1.0.0.beta1"
13
+ gem "typed_operation", "~> 1.0.0.beta2"
14
14
  ```
15
15
 
16
16
  ## Features
@@ -44,15 +44,16 @@ class ShelveBookOperation < ::TypedOperation::Base
44
44
  # Or if you prefer:
45
45
  # `param :description, String`
46
46
 
47
- named_param :author_id, Integer, &:to_i
48
- named_param :isbn, String
47
+ # `param` creates named parameters by default
48
+ param :author_id, Integer, &:to_i
49
+ param :isbn, String
49
50
 
50
51
  # Optional parameters are specified by wrapping the type constraint in the `optional` method, or using the `optional:` option
51
- named_param :shelf_code, optional(Integer)
52
+ param :shelf_code, optional(Integer)
52
53
  # Or if you prefer:
53
54
  # `named_param :shelf_code, Integer, optional: true`
54
55
 
55
- named_param :category, String, default: "unknown".freeze
56
+ param :category, String, default: "unknown".freeze
56
57
 
57
58
  # optional hook called when the operation is initialized, and after the parameters have been set
58
59
  def prepare
@@ -521,7 +522,8 @@ This is an example of a `ApplicationOperation` in a Rails app that uses `Dry::Mo
521
522
 
522
523
  class ApplicationOperation < ::TypedOperation::Base
523
524
  # We choose to use dry-monads for our operations, so include the required modules
524
- include Dry::Monads[:result, :do]
525
+ include Dry::Monads[:result]
526
+ include Dry::Monads::Do.for(:perform)
525
527
 
526
528
  class << self
527
529
  # Setup our own preferred names for the DSL methods
@@ -564,6 +566,8 @@ end
564
566
 
565
567
  ### Using with Action Policy (`action_policy` gem)
566
568
 
569
+ > Note, this optional feature requires the `action_policy` gem to be installed and does not yet work with `ImmutableBase`.
570
+
567
571
  Add `TypedOperation::ActionPolicyAuth` to your `ApplicationOperation` (first `require` the module):
568
572
 
569
573
  ```ruby
@@ -709,40 +713,6 @@ end
709
713
 
710
714
  Note you are provided the ActionPolicy error object, but you cannot stop the error from being re-raised.
711
715
 
712
- ### Using with `literal` monads
713
-
714
- You can use the `literal` gem to provide a `Result` type for your operations.
715
-
716
- ```ruby
717
- class MyOperation < ::TypedOperation::Base
718
- param :account_name, String
719
- param :owner, String
720
-
721
- def perform
722
- create_account.bind do |account|
723
- associate_owner(account).map { account }
724
- end
725
- end
726
-
727
- private
728
-
729
- def create_account
730
- # ...
731
- # Literal::Failure.new(:cant_create_account)
732
- Literal::Success.new(account_name)
733
- end
734
-
735
- def associate_owner(account)
736
- # ...
737
- Literal::Failure.new(:cant_associate_owner)
738
- # Literal::Success.new("ok")
739
- end
740
- end
741
-
742
- MyOperation.new(account_name: "foo", owner: "bar").call
743
- # => Literal::Failure(:cant_associate_owner)
744
- ```
745
-
746
716
  ### Using with `Dry::Monads`
747
717
 
748
718
  As per the example in [`Dry::Monads` documentation](https://dry-rb.org/gems/dry-monads/1.0/do-notation/)
@@ -750,14 +720,14 @@ As per the example in [`Dry::Monads` documentation](https://dry-rb.org/gems/dry-
750
720
  ```ruby
751
721
  class MyOperation < ::TypedOperation::Base
752
722
  include Dry::Monads[:result]
753
- include Dry::Monads::Do.for(:call)
723
+ include Dry::Monads::Do.for(:perform, :create_account)
754
724
 
755
725
  param :account_name, String
756
726
  param :owner, ::Owner
757
727
 
758
728
  def perform
759
729
  account = yield create_account(account_name)
760
- yield associate_owner(account, owner)
730
+ yield AnotherOperation.call(account, owner)
761
731
 
762
732
  Success(account)
763
733
  end
@@ -797,8 +767,11 @@ bin/rails g typed_operation:install
797
767
  Use the `--dry_monads` switch to `include Dry::Monads[:result]` into your `ApplicationOperation` (don't forget to also
798
768
  add `gem "dry-monads"` to your Gemfile)
799
769
 
770
+ Use the `--action_policy` switch to add the `TypedOperation::ActionPolicyAuth` module to your `ApplicationOperation`
771
+ (and you will also need to add `gem "action_policy"` to your Gemfile).
772
+
800
773
  ```ruby
801
- bin/rails g typed_operation:install --dry_monads
774
+ bin/rails g typed_operation:install --dry_monads --action_policy
802
775
  ```
803
776
 
804
777
  ## Generate a new Operation
@@ -12,6 +12,7 @@ rails generate typed_operation:install
12
12
  Options:
13
13
  --------
14
14
  --dry_monads: if specified the ApplicationOperation will include dry-monads Result and Do notation.
15
+ --action_policy: if specified the ApplicationOperation will include action_policy authorization integration.
15
16
 
16
17
  Example:
17
18
  --------
@@ -6,6 +6,7 @@ module TypedOperation
6
6
  module Install
7
7
  class InstallGenerator < Rails::Generators::Base
8
8
  class_option :dry_monads, type: :boolean, default: false
9
+ class_option :action_policy, type: :boolean, default: false
9
10
 
10
11
  source_root File.expand_path("templates", __dir__)
11
12
 
@@ -18,6 +19,10 @@ module TypedOperation
18
19
  def include_dry_monads?
19
20
  options[:dry_monads]
20
21
  end
22
+
23
+ def include_action_policy?
24
+ options[:action_policy]
25
+ end
21
26
  end
22
27
  end
23
28
  end
@@ -1,14 +1,36 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ApplicationOperation < ::TypedOperation::Base
4
+ <% if include_action_policy? -%>
5
+ include TypedOperation::ActionPolicyAuth
6
+
7
+ <% end -%>
4
8
  <% if include_dry_monads? -%>
5
- include Dry::Monads[:result, :do]
9
+ include Dry::Monads[:result]
10
+ include Dry::Monads::Do.for(:perform)
6
11
 
12
+ # Helper to execute then unwrap a successful result or raise an exception
7
13
  def call!
8
14
  call.value!
9
15
  end
10
16
 
11
17
  <% end -%>
12
18
  # Other common parameters & methods for Operations of this application...
13
- # ...
19
+ # Some examples:
20
+ #
21
+ # def self.operation_key
22
+ # name.underscore.to_sym
23
+ # end
24
+ #
25
+ # def operation_key
26
+ # self.class.operation_key
27
+ # end
28
+ #
29
+ # # Translation and localization
30
+ #
31
+ # def translate(key, **)
32
+ # key = "operations.#{operation_key}.#{key}" if key.start_with?(".")
33
+ # I18n.t(key, **)
34
+ # end
35
+ # alias_method :t, :translate
14
36
  end
@@ -8,18 +8,6 @@ module TypedOperation
8
8
 
9
9
  include Operations::Lifecycle
10
10
  include Operations::Callable
11
- include Operations::Deconstruct
12
11
  include Operations::Executable
13
-
14
- class << self
15
- def attribute(name, type, special = nil, reader: :public, writer: :public, positional: false, default: nil)
16
- super(name, type, special, reader:, writer: false, positional:, default:)
17
- end
18
- end
19
-
20
- def with(...)
21
- # copy to new operation with new attrs
22
- self.class.new(**attributes.merge(...))
23
- end
24
12
  end
25
13
  end
@@ -8,7 +8,6 @@ module TypedOperation
8
8
 
9
9
  include Operations::Lifecycle
10
10
  include Operations::Callable
11
- include Operations::Deconstruct
12
11
  include Operations::Executable
13
12
  end
14
13
  end
@@ -5,25 +5,23 @@ module TypedOperation
5
5
  # Introspection methods
6
6
  module Introspection
7
7
  def positional_parameters
8
- literal_attributes.filter_map { |name, attribute| name if attribute.positional? }
8
+ literal_properties.filter_map { |property| property.name if property.positional? }
9
9
  end
10
10
 
11
11
  def keyword_parameters
12
- literal_attributes.filter_map { |name, attribute| name unless attribute.positional? }
12
+ literal_properties.filter_map { |property| property.name if property.keyword? }
13
13
  end
14
14
 
15
15
  def required_parameters
16
- literal_attributes.filter do |name, attribute|
17
- attribute.default.nil? # Any optional parameters will have a default value/proc in their Literal::Attribute
18
- end
16
+ literal_properties.filter { |property| property.required? }
19
17
  end
20
18
 
21
19
  def required_positional_parameters
22
- required_parameters.filter_map { |name, attribute| name if attribute.positional? }
20
+ required_parameters.filter_map { |property| property.name if property.positional? }
23
21
  end
24
22
 
25
23
  def required_keyword_parameters
26
- required_parameters.filter_map { |name, attribute| name unless attribute.positional? }
24
+ required_parameters.filter_map { |property| property.name if property.keyword? }
27
25
  end
28
26
 
29
27
  def optional_positional_parameters
@@ -4,7 +4,7 @@ module TypedOperation
4
4
  module Operations
5
5
  module Lifecycle
6
6
  # This is called by Literal on initialization of underlying Struct/Data
7
- def after_initialization
7
+ def after_initialize
8
8
  prepare if respond_to?(:prepare)
9
9
  end
10
10
  end
@@ -4,10 +4,15 @@ module TypedOperation
4
4
  module Operations
5
5
  # Method to define parameters for your operation.
6
6
  module Parameters
7
+ # Override literal `prop` to prevent creating writers (Literal::Data does this by default)
8
+ def self.prop(name, type, kind = :keyword, reader: :public, writer: :public, default: nil)
9
+ super(name, type, kind, reader:, writer: false, default:)
10
+ end
11
+
7
12
  # Parameter for keyword argument, or a positional argument if you use positional: true
8
13
  # Required, but you can set a default or use optional: true if you want optional
9
14
  def param(name, signature = :any, **options, &converter)
10
- AttributeBuilder.new(self, name, signature, options).define(&converter)
15
+ PropertyBuilder.new(self, name, signature, options).define(&converter)
11
16
  end
12
17
 
13
18
  # Alternative DSL
@@ -2,13 +2,13 @@
2
2
 
3
3
  module TypedOperation
4
4
  module Operations
5
- class AttributeBuilder
5
+ class PropertyBuilder
6
6
  def initialize(typed_operation, parameter_name, type_signature, options)
7
7
  @typed_operation = typed_operation
8
8
  @name = parameter_name
9
9
  @signature = type_signature
10
- @optional = options[:optional]
11
- @positional = options[:positional]
10
+ @optional = options[:optional] # Wraps signature in NilableType
11
+ @positional = options[:positional] # Changes kind to positional
12
12
  @reader = options[:reader] || :public
13
13
  @default_key = options.key?(:default)
14
14
  @default = options[:default]
@@ -23,11 +23,11 @@ module TypedOperation
23
23
  else
24
24
  converter
25
25
  end
26
- @typed_operation.attribute(
26
+ @typed_operation.prop(
27
27
  @name,
28
28
  @signature,
29
+ @positional ? :positional : :keyword,
29
30
  default: default_value_for_literal,
30
- positional: @positional,
31
31
  reader: @reader,
32
32
  &coerce_by
33
33
  )
@@ -51,7 +51,7 @@ module TypedOperation
51
51
  end
52
52
 
53
53
  def union_with_nil_to_support_nil_default
54
- @signature = Literal::Union.new(@signature, NilClass) if has_default_value_nil?
54
+ @signature = Literal::Types::UnionType.new(@signature, NilClass) if has_default_value_nil?
55
55
  end
56
56
 
57
57
  def has_default_value_nil?
@@ -1,3 +1,3 @@
1
1
  module TypedOperation
2
- VERSION = "1.0.0.beta1"
2
+ VERSION = "1.0.0.beta2"
3
3
  end
@@ -11,8 +11,7 @@ require "typed_operation/operations/parameters"
11
11
  require "typed_operation/operations/partial_application"
12
12
  require "typed_operation/operations/callable"
13
13
  require "typed_operation/operations/lifecycle"
14
- require "typed_operation/operations/deconstruct"
15
- require "typed_operation/operations/attribute_builder"
14
+ require "typed_operation/operations/property_builder"
16
15
  require "typed_operation/operations/executable"
17
16
  require "typed_operation/curried"
18
17
  require "typed_operation/immutable_base"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typed_operation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta1
4
+ version: 1.0.0.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Ierodiaconou
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-26 00:00:00.000000000 Z
11
+ date: 2024-06-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Command pattern, which is callable, and can be partially applied, curried
14
14
  and has typed parameters. Authorization to execute via action_policy if desired.
@@ -32,14 +32,13 @@ files:
32
32
  - lib/typed_operation/base.rb
33
33
  - lib/typed_operation/curried.rb
34
34
  - lib/typed_operation/immutable_base.rb
35
- - lib/typed_operation/operations/attribute_builder.rb
36
35
  - lib/typed_operation/operations/callable.rb
37
- - lib/typed_operation/operations/deconstruct.rb
38
36
  - lib/typed_operation/operations/executable.rb
39
37
  - lib/typed_operation/operations/introspection.rb
40
38
  - lib/typed_operation/operations/lifecycle.rb
41
39
  - lib/typed_operation/operations/parameters.rb
42
40
  - lib/typed_operation/operations/partial_application.rb
41
+ - lib/typed_operation/operations/property_builder.rb
43
42
  - lib/typed_operation/partially_applied.rb
44
43
  - lib/typed_operation/prepared.rb
45
44
  - lib/typed_operation/railtie.rb
@@ -58,14 +57,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
58
57
  requirements:
59
58
  - - ">="
60
59
  - !ruby/object:Gem::Version
61
- version: '3.1'
60
+ version: '3.2'
62
61
  required_rubygems_version: !ruby/object:Gem::Requirement
63
62
  requirements:
64
- - - ">"
63
+ - - ">="
65
64
  - !ruby/object:Gem::Version
66
- version: 1.3.1
65
+ version: '0'
67
66
  requirements: []
68
- rubygems_version: 3.4.19
67
+ rubygems_version: 3.5.3
69
68
  signing_key:
70
69
  specification_version: 4
71
70
  summary: TypedOperation is a command pattern with typed parameters, which is callable,
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TypedOperation
4
- module Operations
5
- module Deconstruct
6
- def deconstruct
7
- attributes.values
8
- end
9
-
10
- def deconstruct_keys(keys)
11
- h = attributes.to_h
12
- keys ? h.slice(*keys) : h
13
- end
14
- end
15
- end
16
- end