typed_operation 0.2.0 → 0.4.0

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: 53850ead13d940ad019c674a59e7831ba89da9ec70168d6a81de50711f343523
4
- data.tar.gz: e76a1321066eff1a9034aeaab66140d58732eeab7adf30ff8d51f455ce68f483
3
+ metadata.gz: c80a027434c29b23257b43e03eeb8acb61a3ac300a59e0c04068ffada1dd4aa6
4
+ data.tar.gz: 6b78a70b2a46710746129f8dac3a403bdc692c761c93f3ec111bf09efec8a70e
5
5
  SHA512:
6
- metadata.gz: 057de15499ac13647de16cd3905d23ddf390b00d0951449061e1fc30020ba0c2e546d1c1f38495911012177604740f5c3947426c94ee9dd9763f94df9629ae17
7
- data.tar.gz: f73dd9c04f0e5c3f87861c4d086059d595c296cc85783a85d9c13ef03f04ff7f87f46a4ec0b5cb1f9e9123d9f23ee0274a61d89f534ac666ad778db2f592dee0
6
+ metadata.gz: 35baa03191cfa29e1786de975d7f8d2baf62040859922544f0d79109a8f088996521e85e3dce0c66bde1f7dd5bb2b62a5332cb7841a63a1ebe4b369d48005bb6
7
+ data.tar.gz: ebf8aea46fb0d3dcd72aa846b772f228aca21be2e04695989266218e9303e9b7760770d67f53f57ca231b41b7f502ad5145bf621a0648c6706b6e741a8e6c80e
data/README.md CHANGED
@@ -1,13 +1,10 @@
1
1
  # TypedOperation
2
- Short description and motivation.
3
-
4
- ## Usage
5
2
 
6
3
  An implementation of a Command pattern, which is callable, and can be partially applied (curried).
7
4
 
8
5
  Inputs to the operation are specified as typed attributes using the `param` method.
9
6
 
10
- Results of the operation are a type of `Dry::Monads::Result` object.
7
+ Result format of the operation is up to you, but plays nicely with `Dry::Monads`.
11
8
 
12
9
  ### Examples:
13
10
 
@@ -15,6 +12,8 @@ A base operation class:
15
12
 
16
13
  ```ruby
17
14
  class ApplicationOperation < ::TypedOperation::Base
15
+ include Dry::Monads[:result, :do]
16
+
18
17
  param :initiator, ::RegisteredUser, allow_nil: true
19
18
 
20
19
  private
@@ -96,6 +95,35 @@ Or install it yourself as:
96
95
  $ gem install typed_operation
97
96
  ```
98
97
 
98
+ ### Add an `ApplicationOperation` to your project
99
+
100
+ ```ruby
101
+ bin/rails g typed_operation:install
102
+ ```
103
+
104
+ Use the `--dry_monads` switch to `include Dry::Monads[:result]` into your `ApplicationOperation` (don't forget to also
105
+ add `gem "dry-monads"` to your Gemfile)
106
+
107
+ ```ruby
108
+ bin/rails g typed_operation:install --dry_monads
109
+ ```
110
+
111
+ ## Generate a new Operation
112
+
113
+ ```ruby
114
+ bin/rails g typed_operation TestOperation
115
+ ```
116
+
117
+ You can optionally specify the directory to generate the operation in:
118
+
119
+ ```ruby
120
+ bin/rails g typed_operation TestOperation --path=app/operations
121
+ ```
122
+
123
+ The default path is `app/operations`.
124
+
125
+ The generator will also create a test file.
126
+
99
127
  ## Contributing
100
128
  Contribution directions go here.
101
129
 
@@ -0,0 +1,25 @@
1
+ Operation Generator
2
+ ===================
3
+
4
+ Description:
5
+ ------------
6
+ This generator creates an 'TypedOperation' class that inherits from ApplicationOperation.
7
+
8
+ Command:
9
+ --------
10
+ rails generate typed_operation NAME [options]
11
+
12
+ Arguments:
13
+ ----------
14
+ NAME: The class name of the Operation class to be generated, e.g. 'MyOperation'.
15
+
16
+ Options:
17
+ --------
18
+ --path=path/to/directory: The path to the directory in which the operation class should be generated. The generated class will be namespaced according to the provided path.
19
+
20
+ Example:
21
+ --------
22
+ rails generate typed_operation MyOperation --path=app/operations
23
+
24
+ This will create the following file:
25
+ app/operations/my_operation.rb
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% if namespace_name.present? %>
4
+ module <%= namespace_name %>
5
+ class <%= name %> < ::ApplicationOperation
6
+ # Replace with implementation...
7
+ param :my_param, String, convert: true
8
+ param :an_optional_param, Integer, allow_nil: true
9
+
10
+ def prepare
11
+ # Prepare...
12
+ end
13
+
14
+ def call
15
+ # Perform...
16
+ "Hello World!"
17
+ end
18
+ end
19
+ end
20
+ <% else %>
21
+ class <%= name %> < ::ApplicationOperation
22
+ # Replace with implementation...
23
+ param :my_param, String, convert: true
24
+ param :an_optional_param, Integer, allow_nil: true
25
+
26
+ def prepare
27
+ # Prepare...
28
+ end
29
+
30
+ def call
31
+ # Perform...
32
+ "Hello World!"
33
+ end
34
+ end
35
+ <% end %>
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% if namespace_name.present? %>
4
+ module <%= namespace_name %>
5
+ class <%= name %>Test < ActiveSupport::TestCase
6
+ def setup
7
+ @operation = <%= name %>.new(my_param: "test")
8
+ end
9
+
10
+ test 'should raise ParameterError if my_param is nil' do
11
+ assert_raises(ParameterError) do
12
+ <%= name %>.new(my_param: nil)
13
+ end
14
+ end
15
+
16
+ test 'should convert my_param if it is not a string' do
17
+ assert_equal <%= name %>.new(my_param: 123).my_param, "123"
18
+ end
19
+
20
+ test 'call returns after operation' do
21
+ result = @operation.call
22
+ assert_equal result, "Hello World!"
23
+ end
24
+ end
25
+ end
26
+ <% else %>
27
+ class <%= name %>Test < ActiveSupport::TestCase
28
+ def setup
29
+ @operation = <%= name %>.new(my_param: "test")
30
+ end
31
+
32
+ test 'should raise ParameterError if my_param is nil' do
33
+ assert_raises(ParameterError) do
34
+ <%= name %>.new(my_param: nil)
35
+ end
36
+ end
37
+
38
+ test 'should convert my_param if it is not a string' do
39
+ assert_equal <%= name %>.new(my_param: 123).my_param, "123"
40
+ end
41
+
42
+ test 'call returns after operation' do
43
+ result = @operation.call
44
+ assert_equal result, "Hello World!"
45
+ end
46
+ end
47
+ <% end %>
@@ -0,0 +1,21 @@
1
+ Install Generator
2
+ =================
3
+
4
+ Description:
5
+ ------------
6
+ This generator installs a `ApplicationOperation` class which is your base class for all Operations.
7
+
8
+ Command:
9
+ --------
10
+ rails generate typed_operation:install
11
+
12
+ Options:
13
+ --------
14
+ --dry_monads: if specified the ApplicationOperation will include dry-monads Result and Do notation.
15
+
16
+ Example:
17
+ --------
18
+ rails generate typed_operation:install
19
+
20
+ This will create the following file:
21
+ app/operations/application_operation.rb
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module TypedOperation
6
+ module Install
7
+ class InstallGenerator < Rails::Generators::Base
8
+ class_option :dry_monads, type: :boolean, default: false
9
+
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ def copy_application_operation_file
13
+ template "application_operation.rb", "app/operations/application_operation.rb"
14
+ end
15
+
16
+ private
17
+
18
+ def include_dry_monads?
19
+ options[:dry_monads]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ApplicationOperation < ::TypedOperation::Base
4
+ <% if include_dry_monads? -%>
5
+ include Dry::Monads[:result, :do]
6
+
7
+ def call!
8
+ call.value!
9
+ end
10
+
11
+ <% end -%>
12
+ # Other common parameters & methods for Operations of this application...
13
+ # ...
14
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/named_base"
4
+
5
+ class TypedOperationGenerator < Rails::Generators::NamedBase
6
+ source_root File.expand_path("templates", __dir__)
7
+
8
+ class_option :path, type: :string, default: "app/operations"
9
+
10
+ def generate_operation
11
+ template(
12
+ File.join(self.class.source_root, "operation.rb"),
13
+ File.join(options[:path], "#{file_name}.rb")
14
+ )
15
+ template(
16
+ File.join(self.class.source_root, "operation_test.rb"),
17
+ File.join("test/", options[:path].gsub(/\Aapp\//, ""), "#{file_name}_test.rb")
18
+ )
19
+ end
20
+
21
+ private
22
+
23
+ def namespace_name
24
+ namespace_path = options[:path].gsub(/^app\/[^\/]*\//, "")
25
+ namespace_path.split("/").map(&:camelize).join("::")
26
+ end
27
+ end
@@ -1,11 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry/monads"
3
+ require "vident/typed"
4
4
  require "vident/typed/attributes"
5
5
 
6
6
  module TypedOperation
7
7
  class Base
8
- include Dry::Monads[:result, :do]
9
8
  include Vident::Typed::Attributes
10
9
 
11
10
  class << self
@@ -34,7 +33,11 @@ module TypedOperation
34
33
  end
35
34
 
36
35
  def initialize(**attributes)
37
- prepare_attributes(attributes)
36
+ begin
37
+ prepare_attributes(attributes)
38
+ rescue ::Dry::Struct::Error => e
39
+ raise ParameterError, e.message
40
+ end
38
41
  prepare if respond_to?(:prepare)
39
42
  end
40
43
 
@@ -42,10 +45,6 @@ module TypedOperation
42
45
  raise NotImplementedError, "You must implement #call"
43
46
  end
44
47
 
45
- def call!
46
- call.value!
47
- end
48
-
49
48
  def to_proc
50
49
  method(:call).to_proc
51
50
  end
@@ -1,4 +1,8 @@
1
1
  module TypedOperation
2
2
  class Railtie < ::Rails::Railtie
3
+ generators do
4
+ require "generators/typed_operation/install/install_generator"
5
+ require "generators/typed_operation_generator"
6
+ end
3
7
  end
4
8
  end
@@ -1,3 +1,3 @@
1
1
  module TypedOperation
2
- VERSION = "0.2.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -4,7 +4,6 @@ require "typed_operation/base"
4
4
  require "typed_operation/partially_applied"
5
5
  require "typed_operation/prepared"
6
6
 
7
-
8
7
  module TypedOperation
9
- # Your code goes here...
8
+ class ParameterError < StandardError; end
10
9
  end
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: 0.2.0
4
+ version: 0.4.0
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-05-04 00:00:00.000000000 Z
11
+ date: 2023-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,7 +16,7 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '7.0'
19
+ version: '6.0'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
22
  version: '8.0'
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ">="
28
28
  - !ruby/object:Gem::Version
29
- version: '7.0'
29
+ version: '6.0'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
32
  version: '8.0'
@@ -45,26 +45,21 @@ dependencies:
45
45
  - !ruby/object:Gem::Version
46
46
  version: 0.1.0
47
47
  - !ruby/object:Gem::Dependency
48
- name: dry-monads
48
+ name: dry-initializer
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">"
52
- - !ruby/object:Gem::Version
53
- version: '1'
54
- - - "<"
51
+ - - "~>"
55
52
  - !ruby/object:Gem::Version
56
- version: '2'
53
+ version: '3.0'
57
54
  type: :runtime
58
55
  prerelease: false
59
56
  version_requirements: !ruby/object:Gem::Requirement
60
57
  requirements:
61
- - - ">"
62
- - !ruby/object:Gem::Version
63
- version: '1'
64
- - - "<"
58
+ - - "~>"
65
59
  - !ruby/object:Gem::Version
66
- version: '2'
67
- description: TypedOperation is a command pattern implementation
60
+ version: '3.0'
61
+ description: TypedOperation is a command pattern implementation where inputs can be
62
+ defined with runtime type checks. Operations can be partially applied.
68
63
  email:
69
64
  - stevegeek@gmail.com
70
65
  executables: []
@@ -74,6 +69,13 @@ files:
74
69
  - MIT-LICENSE
75
70
  - README.md
76
71
  - Rakefile
72
+ - lib/generators/USAGE
73
+ - lib/generators/templates/operation.rb
74
+ - lib/generators/templates/operation_test.rb
75
+ - lib/generators/typed_operation/install/USAGE
76
+ - lib/generators/typed_operation/install/install_generator.rb
77
+ - lib/generators/typed_operation/install/templates/application_operation.rb
78
+ - lib/generators/typed_operation_generator.rb
77
79
  - lib/tasks/typed_operation_tasks.rake
78
80
  - lib/typed_operation.rb
79
81
  - lib/typed_operation/base.rb