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 +4 -4
- data/README.md +16 -43
- data/lib/generators/typed_operation/install/USAGE +1 -0
- data/lib/generators/typed_operation/install/install_generator.rb +5 -0
- data/lib/generators/typed_operation/install/templates/application_operation.rb +24 -2
- data/lib/typed_operation/base.rb +0 -12
- data/lib/typed_operation/immutable_base.rb +0 -1
- data/lib/typed_operation/operations/introspection.rb +5 -7
- data/lib/typed_operation/operations/lifecycle.rb +1 -1
- data/lib/typed_operation/operations/parameters.rb +6 -1
- data/lib/typed_operation/operations/{attribute_builder.rb → property_builder.rb} +6 -6
- data/lib/typed_operation/version.rb +1 -1
- data/lib/typed_operation.rb +1 -2
- metadata +7 -8
- data/lib/typed_operation/operations/deconstruct.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 501477c614c107723d1c915099dd06209336708ba5e9672c26622d02a9cd4782
|
4
|
+
data.tar.gz: b5ab846baf478e3cce3fd8ea1752c04ee708c9a7f84d2be6d47d4c9a94505ac5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
-
|
48
|
-
|
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
|
-
|
52
|
+
param :shelf_code, optional(Integer)
|
52
53
|
# Or if you prefer:
|
53
54
|
# `named_param :shelf_code, Integer, optional: true`
|
54
55
|
|
55
|
-
|
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
|
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(:
|
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
|
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
|
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
|
data/lib/typed_operation/base.rb
CHANGED
@@ -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
|
@@ -5,25 +5,23 @@ module TypedOperation
|
|
5
5
|
# Introspection methods
|
6
6
|
module Introspection
|
7
7
|
def positional_parameters
|
8
|
-
|
8
|
+
literal_properties.filter_map { |property| property.name if property.positional? }
|
9
9
|
end
|
10
10
|
|
11
11
|
def keyword_parameters
|
12
|
-
|
12
|
+
literal_properties.filter_map { |property| property.name if property.keyword? }
|
13
13
|
end
|
14
14
|
|
15
15
|
def required_parameters
|
16
|
-
|
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 { |
|
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 { |
|
24
|
+
required_parameters.filter_map { |property| property.name if property.keyword? }
|
27
25
|
end
|
28
26
|
|
29
27
|
def optional_positional_parameters
|
@@ -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
|
-
|
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
|
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.
|
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::
|
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?
|
data/lib/typed_operation.rb
CHANGED
@@ -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/
|
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.
|
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:
|
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.
|
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:
|
65
|
+
version: '0'
|
67
66
|
requirements: []
|
68
|
-
rubygems_version: 3.
|
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
|