active_operation 0.2.0 → 1.0.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
- SHA1:
3
- metadata.gz: eead73f1ad502fdd329a6cd5ae6acc20306472df
4
- data.tar.gz: c4adb13fbb34e04cf4895175babc9d458ff8c6bc
2
+ SHA256:
3
+ metadata.gz: 0af2d62196c5a9ae917f9071afe1cc8b861929b75d9508f1f6193e8a87509f07
4
+ data.tar.gz: 15861fe07258fa8a11bacdc0d9c578da175070bba359122cf8446fce8958ea3e
5
5
  SHA512:
6
- metadata.gz: 32c999e6bd87a939c985ea696250cf00de302fe0b7749edb07339381e1a543c4baaa54bc10b3101b0215f34f9955e9bf13d555ac581a693d276f029015ad0d82
7
- data.tar.gz: ac35417f23a2a4321e11869b3c95f8a0120da18cf4b71cbae7ad22494d3eb04b8638f2d119cdaa960cb25c84de908bc63a9a3fb4b9eba736e0b385ecd8afb6fe
6
+ metadata.gz: a16400a73363829404d5767b189061319bb76a620af86b30af62ebf7537d90344ca7ab167cd27ad92fb1230c0779e1ab5c1ae0b32f90d4472647b490d21ecffa
7
+ data.tar.gz: 661062ef56212fc48c83cdd512728148c4b967bea8991f87ae60f845420eb5b851faa9b0c862a6b8a150a3af59c5cf26d2322eb729c9f077b6eedd8a2513b06f
@@ -0,0 +1,26 @@
1
+ name: Testing
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+ branches: [master]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test:
12
+ name: Ruby v${{ matrix.ruby_version}}
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ ruby_version: [2.6.9, 2.7.5, 3.0.3]
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - name: Set up Ruby
20
+ uses: ruby/setup-ruby@v1
21
+ with:
22
+ ruby-version: ${{ matrix.ruby_version }}
23
+ - name: Install dependencies
24
+ run: bundle install
25
+ - name: Run tests
26
+ run: bundle exec rake
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Konstantin Tennhard
3
+ Copyright (c) 2016-2021 Konstantin Tennhard
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # ActiveOperation
2
+
2
3
  [![Gem Version](https://badge.fury.io/rb/active_operation.svg)](https://rubygems.org/gems/active_operation)
3
4
  [![Downloads](http://ruby-gem-downloads-badge.herokuapp.com/active_operation?type=total)](https://rubygems.org/gems/active_operation)
4
5
 
5
6
  `ActiveOperation` is a micro-framework for modelling business processes.
6
7
  It is the perfect companion for any Rails application.
7
- The core idea behind an operation is to move code that usually would either live in a controller or a model into a dedicated object.
8
+ The main idea behind an operation is to move code that traditionally either lives in a controller or a model into a dedicated object.
9
+ Multiple operations can be combined into a pipeline.
10
+ This helps with structuring large business processes and aids reusability.
8
11
 
9
12
  ## Installation
10
13
 
11
14
  Add this line to your application's Gemfile:
12
15
 
13
16
  ```ruby
14
- gem 'active_operation', '~> 0.1.0'
17
+ gem 'active_operation'
15
18
  ```
16
19
 
17
20
  And then execute:
@@ -20,7 +23,7 @@ And then execute:
20
23
  $ bundle
21
24
  ```
22
25
 
23
- Or install it yourself as:
26
+ Or install it manually as:
24
27
 
25
28
  ```
26
29
  $ gem install active_operation
@@ -28,20 +31,24 @@ $ gem install active_operation
28
31
 
29
32
  ### Rails
30
33
 
31
- We recommend running the install generator to initialize a base operation:
34
+ Run the the install generator to initialize a base operation:
32
35
 
33
36
  ```
34
37
  rails g active_operation:install
35
38
  ```
36
39
 
37
- You can also generate new operations using:
40
+ Then generate the desired operation:
38
41
 
39
42
  ```
40
- rails g active_operation:operation Signup
43
+ rails g active_operation:operation signup/create_user
41
44
  ```
42
45
 
43
46
  ## Usage
44
47
 
48
+ We will first look at defining and using a single operation and then explore how to combine multiple operations into a pipeline.
49
+
50
+ ### Defining and using a single operation
51
+
45
52
  To define an operation, create a new class and inherit from `ActiveOperation::Base`.
46
53
  The input arguments of an operation are defined using the `input` statement.
47
54
  These statements describe the arguments the initializer takes.
@@ -76,8 +83,8 @@ end
76
83
  ```
77
84
 
78
85
  To execute an operation, instantiate it and invoke the `#call` method.
79
- As a result, this method will return the operation's output, which is also available through the `#output` method.
80
- For convenience, operations also expose a `.call` class method, which instantiates the operation, runs it and returns the its output.
86
+ This method will return the operation's output, which is also available through the `#output` method.
87
+ For convenience, operations also expose a `.call` class method, which instantiates the operation, runs it and returns its output.
81
88
 
82
89
  Operations go through different states during their lifecycle.
83
90
  After executing an operation, the state can be used for branching purposes.
@@ -106,17 +113,43 @@ class UsersController < ApplicationController
106
113
  end
107
114
  ```
108
115
 
116
+ ### Defining and using pipelines
117
+
118
+ Continuing with the example above, given the two operations, `User::Signup` and `Email::SendWelcomeEmail`, a pipeline can be defined as follows:
119
+
120
+ ```ruby
121
+ class OnboardUser < ActiveSupport::Pipeline
122
+ use User::Signup
123
+ use Email::SendWelcomeEmail
124
+ end
125
+ ```
126
+
127
+ The pipeline derives its inputs from the first operation – in this case `User::Signup`.
128
+ Similarly, the pipeline's output is simply the output of the last operation.
129
+ All operations beyond the first operation are expected to take its predecessors output as input.
130
+ In the example above, `User::Signup` produces a `User` record that `Email::SendWelcomeEmail` takes as input.
131
+
132
+ Pipeline themselves are operations and can therefore be invoked like any other operations.
133
+
134
+ ```ruby
135
+ OnboardUser.call(email: "john@doe.com", password: "123456")
136
+ ```
137
+
138
+ Furthermore, since pipelines are operations themselves, they can also be used within other pipelines.
139
+
109
140
  ## Development
110
141
 
111
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
142
+ After checking out the repo, run `bin/setup` to install dependencies.
143
+ Then, run `rake spec` to run the tests.
144
+ You can also run `bin/console` for an interactive prompt that will allow you to experiment.
112
145
 
113
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
146
+ To install this gem onto your local machine, run `bundle exec rake install`.
147
+ To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
114
148
 
115
149
  ## Contributing
116
150
 
117
151
  Bug reports and pull requests are welcome on GitHub at https://github.com/t6d/active_operation.
118
152
 
119
-
120
153
  ## License
121
154
 
122
155
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["konstantin@tennhard.net", "sebastian.szturo@gmail.com"]
11
11
 
12
12
  spec.summary = %q{ActiveOperation is a micro-framework for modelling business processes.}
13
- spec.homepage = "https://gitub.com/t6d/active_operation"
13
+ spec.homepage = "https://github.com/t6d/active_operation"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -18,11 +18,11 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_runtime_dependency "activesupport", "> 4.0"
22
- spec.add_runtime_dependency "smart_properties", "~> 1.0"
21
+ spec.add_runtime_dependency "activesupport", "> 5.0"
22
+ spec.add_runtime_dependency "smart_properties", "~> 1.17"
23
23
 
24
- spec.add_development_dependency "bundler", "~> 1.12"
25
- spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "bundler", "~> 2.0"
25
+ spec.add_development_dependency "rake", "~> 13.0"
26
26
  spec.add_development_dependency "rspec", "~> 3.0"
27
27
  spec.add_development_dependency "pry"
28
28
  end
data/dev.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ name: active-operation
3
+ up:
4
+ - ruby: 3.0.3
5
+ - bundler
6
+ commands:
7
+ test: bundle exec rake
@@ -20,6 +20,37 @@ class ActiveOperation::Base
20
20
  new(*args).call
21
21
  end
22
22
 
23
+ def from_proc(execute)
24
+ Class.new(self) do
25
+ positional_arguments = []
26
+ keyword_arguments = []
27
+
28
+ execute.parameters.each do |type, name|
29
+ case type
30
+ when :req
31
+ input name, type: :positional, required: true
32
+ positional_arguments << name
33
+ when :keyreq
34
+ input name, type: :keyword, required: true
35
+ keyword_arguments << name
36
+ else
37
+ raise ArgumentError, "Argument type not supported: #{type}"
38
+ end
39
+ end
40
+
41
+ define_method(:execute) do
42
+ args = positional_arguments.map { |name| self[name] }
43
+ opts = keyword_arguments.map { |name| [name, self[name]] }.to_h
44
+
45
+ if opts.empty?
46
+ execute.call(*args)
47
+ else
48
+ execute.call(*args, **opts)
49
+ end
50
+ end
51
+ end
52
+ end
53
+
23
54
  def call(*args)
24
55
  perform(*args)
25
56
  end
@@ -161,6 +161,8 @@ module ActiveOperation
161
161
  end
162
162
  end
163
163
 
164
+ require 'rspec/core' unless defined? RSpec.configure
165
+
164
166
  RSpec.configure do |config|
165
167
  config.include ActiveOperation::Matcher::Execution
166
168
  end
@@ -27,6 +27,8 @@ module ActiveOperation
27
27
  end
28
28
 
29
29
  def use(operation, options = {})
30
+ operation = ActiveOperation::Base.from_proc(operation) if operation.kind_of?(Proc)
31
+
30
32
  if operations.empty?
31
33
  inputs = operation.inputs
32
34
 
@@ -1,3 +1,3 @@
1
1
  module ActiveOperation
2
- VERSION = "0.2.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,3 +1,4 @@
1
+ require "active_support/dependencies/autoload"
1
2
  require "active_support/callbacks"
2
3
  require "delegate"
3
4
  require "smart_properties"
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
  require 'rails/generators/active_record'
3
4
 
@@ -7,7 +8,7 @@ module ActiveOperation
7
8
  source_root File.expand_path('../../../../../support/templates', __FILE__)
8
9
 
9
10
  def create_application_operation
10
- template 'application_operation.rb', 'app/operations/application_operation.rb'
11
+ template 'application_operation.rb.erb', 'app/operations/application_operation.rb'
11
12
  end
12
13
  end
13
14
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'rails/generators/base'
2
3
  require 'rails/generators/active_record'
3
4
 
@@ -6,8 +7,10 @@ module ActiveOperation
6
7
  class OperationGenerator < Rails::Generators::NamedBase
7
8
  source_root File.expand_path('../../../../../support/templates', __FILE__)
8
9
 
10
+ hook_for :test_framework
11
+
9
12
  def create_operation
10
- template 'operation.rb', File.join('app/operations', class_path, "#{file_name}.rb")
13
+ template 'operation.rb.erb', File.join('app/operations', class_path, "#{file_name}.rb")
11
14
  end
12
15
  end
13
16
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module Rspec
3
+ module Generators
4
+ class OperationGenerator < Rails::Generators::NamedBase
5
+ source_root File.expand_path('../../../../../support/templates', __FILE__)
6
+
7
+ def copy_files
8
+ template 'operation_spec.rb.erb', File.join('spec/operations', class_path, "#{file_name}_spec.rb")
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module TestUnit
3
+ module Generators
4
+ class OperationGenerator < Rails::Generators::NamedBase
5
+ source_root File.expand_path('../../../../../support/templates', __FILE__)
6
+
7
+ def copy_files
8
+ template 'operation_test.rb.erb', File.join('test/operations', class_path, "#{file_name}_test.rb")
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,2 +1,3 @@
1
+ # frozen_string_literal: true
1
2
  class ApplicationOperation < ActiveOperation::Base
2
3
  end
@@ -1,4 +1,5 @@
1
- class <%= name %> < ApplicationOperation
1
+ # frozen_string_literal: true
2
+ class <%= class_name %> < ApplicationOperation
2
3
  # input :email, accepts: String, type: :keyword, required: true
3
4
  # input :password, accepts: String, type: :keyword, required: true
4
5
  #
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+ require 'rails_helper'
3
+
4
+ RSpec.describe <%= class_name %>, type: :operation do
5
+ describe "#execute" do
6
+ it "should be tested"
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ <% module_namespacing do -%>
5
+ class <%= class_name %>Test < ActiveSupport::TestCase
6
+ # test "#execute" do
7
+ # # TODO
8
+ # end
9
+ end
10
+ <% end -%>
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_operation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Tennhard
8
8
  - Sebastian Szturo
9
- autorequire:
9
+ autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2017-06-05 00:00:00.000000000 Z
12
+ date: 2021-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -17,56 +17,56 @@ dependencies:
17
17
  requirements:
18
18
  - - ">"
19
19
  - !ruby/object:Gem::Version
20
- version: '4.0'
20
+ version: '5.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">"
26
26
  - !ruby/object:Gem::Version
27
- version: '4.0'
27
+ version: '5.0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: smart_properties
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: '1.0'
34
+ version: '1.17'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '1.0'
41
+ version: '1.17'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: bundler
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: '1.12'
48
+ version: '2.0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: '1.12'
55
+ version: '2.0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: rake
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
60
  - - "~>"
61
61
  - !ruby/object:Gem::Version
62
- version: '10.0'
62
+ version: '13.0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - "~>"
68
68
  - !ruby/object:Gem::Version
69
- version: '10.0'
69
+ version: '13.0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rspec
72
72
  requirement: !ruby/object:Gem::Requirement
@@ -95,7 +95,7 @@ dependencies:
95
95
  - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
- description:
98
+ description:
99
99
  email:
100
100
  - konstantin@tennhard.net
101
101
  - sebastian.szturo@gmail.com
@@ -103,8 +103,10 @@ executables: []
103
103
  extensions: []
104
104
  extra_rdoc_files: []
105
105
  files:
106
+ - ".github/workflows/testing.yml"
106
107
  - ".gitignore"
107
108
  - ".rspec"
109
+ - ".ruby-version"
108
110
  - ".travis.yml"
109
111
  - Gemfile
110
112
  - LICENSE.txt
@@ -113,6 +115,7 @@ files:
113
115
  - active_operation.gemspec
114
116
  - bin/console
115
117
  - bin/setup
118
+ - dev.yml
116
119
  - lib/active_operation.rb
117
120
  - lib/active_operation/base.rb
118
121
  - lib/active_operation/input.rb
@@ -125,13 +128,17 @@ files:
125
128
  - lib/generators/active_operation/install/install_generator.rb
126
129
  - lib/generators/active_operation/operation/USAGE
127
130
  - lib/generators/active_operation/operation/operation_generator.rb
128
- - support/templates/application_operation.rb
129
- - support/templates/operation.rb
130
- homepage: https://gitub.com/t6d/active_operation
131
+ - lib/generators/rspec/operation/operation_generator.rb
132
+ - lib/generators/test_unit/operation/operation_generator.rb
133
+ - support/templates/application_operation.rb.erb
134
+ - support/templates/operation.rb.erb
135
+ - support/templates/operation_spec.rb.erb
136
+ - support/templates/operation_test.rb.erb
137
+ homepage: https://github.com/t6d/active_operation
131
138
  licenses:
132
139
  - MIT
133
140
  metadata: {}
134
- post_install_message:
141
+ post_install_message:
135
142
  rdoc_options: []
136
143
  require_paths:
137
144
  - lib
@@ -146,9 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
153
  - !ruby/object:Gem::Version
147
154
  version: '0'
148
155
  requirements: []
149
- rubyforge_project:
150
- rubygems_version: 2.6.11
151
- signing_key:
156
+ rubygems_version: 3.2.32
157
+ signing_key:
152
158
  specification_version: 4
153
159
  summary: ActiveOperation is a micro-framework for modelling business processes.
154
160
  test_files: []