piper-rb 0.3.2
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 +7 -0
- data/.github/workflows/ruby.yml +20 -0
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/.rubocop.yml +53 -0
- data/Gemfile +3 -0
- data/README.md +147 -0
- data/bin/piper +130 -0
- data/bin/runner/test_helpers.rb +24 -0
- data/lib/piper/dsl/piper_helpers.rb +53 -0
- data/lib/piper/dsl/piper_pipe.rb +39 -0
- data/lib/piper/dsl/piper_steps.rb +45 -0
- data/lib/piper/hash_helpers.rb +12 -0
- data/lib/piper/piper_service.rb +47 -0
- data/lib/piper/runner_helpers.rb +30 -0
- data/lib/piper/string_helpers.rb +26 -0
- data/lib/piper/templates/.piper.yml.tt +2 -0
- data/lib/piper/templates/base_service.rb.tt +11 -0
- data/lib/piper/templates/named_service.rb.tt +12 -0
- data/lib/piper/templates/named_service_spec.rb.tt +41 -0
- data/lib/piper/templates/named_service_test.rb.tt +17 -0
- data/lib/piper/version.rb +3 -0
- data/lib/piper-rb.rb +4 -0
- data/piper-rb.gemspec +34 -0
- metadata +142 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bec60e8e1f0e658d2d2e301b35f7781a065efd690548357e1cdfe5092482111b
|
4
|
+
data.tar.gz: 3318869c3b540b1fd32d1ca514cccaa11c295729930113fc58f7c82078f3cf13
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2d31fbd1f57669fbf39a13a43496f4ce665eb3c23d5328dfa0b2034ee64075407d14f4bd705b789602893b3ec0e746e9ac37c122db42693314bbbbd87c0c0c4
|
7
|
+
data.tar.gz: 4a64d16c9320ab422931e87a5f8668c4524968ce29c6cfd955b6fbdaa47cdf2068e0d3c1e5e86fbbd6260f064f21157531b2ce04faf5186aa9ec25aa82f5acaa
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
|
10
|
+
steps:
|
11
|
+
- uses: actions/checkout@v2
|
12
|
+
- name: Set up Ruby 2.6
|
13
|
+
uses: actions/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: 2.6.x
|
16
|
+
- name: Build and test with RSpec
|
17
|
+
run: |
|
18
|
+
gem install bundler
|
19
|
+
bundle install --jobs 4 --retry 3
|
20
|
+
bundle exec rspec
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-performance
|
3
|
+
- rubocop-rspec
|
4
|
+
|
5
|
+
AllCops:
|
6
|
+
TargetRubyVersion: 2.5
|
7
|
+
|
8
|
+
# METRICS
|
9
|
+
MethodLength:
|
10
|
+
CountComments: false
|
11
|
+
Max: 15
|
12
|
+
Exclude:
|
13
|
+
- 'spec/**/*'
|
14
|
+
Metrics/LineLength:
|
15
|
+
Enabled: false
|
16
|
+
Metrics/AbcSize:
|
17
|
+
Max: 25
|
18
|
+
Exclude:
|
19
|
+
- 'spec/**/*'
|
20
|
+
Metrics/BlockLength:
|
21
|
+
Exclude:
|
22
|
+
- 'spec/**/*'
|
23
|
+
|
24
|
+
# LAYOUT
|
25
|
+
Layout/DotPosition:
|
26
|
+
EnforcedStyle: leading
|
27
|
+
Enabled: true
|
28
|
+
Layout/ExtraSpacing:
|
29
|
+
Enabled: false
|
30
|
+
Layout/SpaceAroundOperators:
|
31
|
+
Enabled: false
|
32
|
+
Layout/EmptyLinesAroundClassBody:
|
33
|
+
Enabled: false
|
34
|
+
Layout/AccessModifierIndentation:
|
35
|
+
Enabled: false
|
36
|
+
Layout/HashAlignment:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
# STYLE
|
40
|
+
Style/StringLiterals:
|
41
|
+
EnforcedStyle: double_quotes
|
42
|
+
Style/FrozenStringLiteralComment:
|
43
|
+
Enabled: false
|
44
|
+
rubocop: warning
|
45
|
+
|
46
|
+
|
47
|
+
# RSPEC
|
48
|
+
RSpec/ContextWording:
|
49
|
+
Enabled: false
|
50
|
+
RSpec/NamedSubject:
|
51
|
+
Enabled: false
|
52
|
+
RSpec/NestedGroups:
|
53
|
+
Max: 5
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# Piper.rb
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'piper-rb', git: 'https://github.com/ellmo/piper-rb.git'
|
9
|
+
```
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
### super basic
|
14
|
+
|
15
|
+
The most basic element of `Piper` is a `pipe`.
|
16
|
+
Just think of it as a condition step. If no keywords are used, then (in accordance to Ruby
|
17
|
+
conventions) the last line of code is taken as the step's condition:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require "piper-rb"
|
21
|
+
|
22
|
+
class YourSuperbService < PiperService
|
23
|
+
attribute :input, Types::Any
|
24
|
+
|
25
|
+
pipe "input is Numeric" do
|
26
|
+
input.is_a? Numeric
|
27
|
+
end
|
28
|
+
|
29
|
+
pipe "input is greater than 100?" do
|
30
|
+
false
|
31
|
+
|
32
|
+
200 == 100
|
33
|
+
|
34
|
+
input > 100
|
35
|
+
end
|
36
|
+
|
37
|
+
pipe "input is less than 1000?" do
|
38
|
+
input < 1000
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
YourSuperbService.new(input: 200).call
|
43
|
+
#=> Success(true)
|
44
|
+
|
45
|
+
YourSuperbService.new(input: "asd").call
|
46
|
+
#=> Failure({:service=>#<YourSuperbService input="asd">, :object=>false, :message=>nil})
|
47
|
+
|
48
|
+
```
|
49
|
+
|
50
|
+
### keywords for more control
|
51
|
+
|
52
|
+
If you want to specify the condition, object to be returned and the error message, there are
|
53
|
+
keywords for this:
|
54
|
+
|
55
|
+
* `condition` (aliase: `cond`)
|
56
|
+
* `result_object` (aliases: `object`, `rslt`)
|
57
|
+
* `message` (aliases: `fail_message`, `mssg`)
|
58
|
+
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
require "piper-rb"
|
62
|
+
|
63
|
+
class YourFineService < PiperService
|
64
|
+
attribute :input, Types::Any
|
65
|
+
|
66
|
+
pipe "input is Numeric" do
|
67
|
+
message { "The input must be `Numeric`." }
|
68
|
+
|
69
|
+
input.is_a? Numeric
|
70
|
+
end
|
71
|
+
|
72
|
+
pipe "input is greater than 100?" do
|
73
|
+
cond { input > 100 }
|
74
|
+
|
75
|
+
200 == 100
|
76
|
+
end
|
77
|
+
|
78
|
+
pipe "input is less than 1000?" do
|
79
|
+
object { input }
|
80
|
+
|
81
|
+
input < 1000
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
YourFineService.new(input: 200).call
|
86
|
+
#=> Success(200)
|
87
|
+
|
88
|
+
YourFineService.new(input: "asd").call
|
89
|
+
#=> Failure({:service=>(...), :object=>false, :message=>"The input must be `Numeric`."})
|
90
|
+
|
91
|
+
```
|
92
|
+
|
93
|
+
### you can access last step's object with `last_result`:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
require "piper-rb"
|
97
|
+
|
98
|
+
class YourGreatService < PiperService
|
99
|
+
attribute :input, Types::Any
|
100
|
+
|
101
|
+
pipe "this step`s result should be passed to..." do
|
102
|
+
input * 30
|
103
|
+
end
|
104
|
+
|
105
|
+
pipe "...to this step" do
|
106
|
+
cond { last_result == input * 30 }
|
107
|
+
object { last_result }
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
YourGreatService.new(input: 20).call
|
112
|
+
#=> Success(600)
|
113
|
+
```
|
114
|
+
|
115
|
+
### you can nest services, calling them in a pipe passes their result:
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
require "piper-rb"
|
119
|
+
|
120
|
+
class YourMajesticService < PiperService
|
121
|
+
attribute :input, Types::Any
|
122
|
+
|
123
|
+
pipe :nothing_to_see_here do
|
124
|
+
true
|
125
|
+
end
|
126
|
+
|
127
|
+
pipe :calling_nested_service do
|
128
|
+
YourFlamboyantService.new(nested_input: input * 20).call
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class YourFlamboyantService < PiperService
|
133
|
+
attribute :nested_input, Types::Any
|
134
|
+
|
135
|
+
pipe :simple_step do
|
136
|
+
message { "I am the one who knocks!" }
|
137
|
+
nested_input >= 300
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
YourMajesticService.new(input: 20).call
|
143
|
+
#=> Success(true)
|
144
|
+
|
145
|
+
YourMajesticService.new(input: 2).call
|
146
|
+
#=> Failure({:service=>#<YourFlamboyantService nested_input=40>, :object=>false, :message=>"I am the one who knocks!"})
|
147
|
+
```
|
data/bin/piper
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "piper-rb"
|
3
|
+
require "thor"
|
4
|
+
require "yaml"
|
5
|
+
|
6
|
+
require_relative "runner/test_helpers"
|
7
|
+
|
8
|
+
class PiperRunner < Thor
|
9
|
+
include Thor::Actions
|
10
|
+
include PiperRunnerHelpers::TestHelpers
|
11
|
+
|
12
|
+
BASE_SERVICE_FILE_NAME = "base_service.rb".freeze
|
13
|
+
CONFIG_FILE_NAME = ".piper.yml".freeze
|
14
|
+
DEFAULT_BASE_SERVICE = "base_service".freeze
|
15
|
+
DEFAULT_PIPER = "piper".freeze
|
16
|
+
NAMED_SERVICE_FILE_NAME = "named_service.rb".freeze
|
17
|
+
|
18
|
+
NOTE__NO_CUSTOM_CONF = "No custom config found. Using `PiperService` directly.".freeze
|
19
|
+
NOTE__DEFAULT_BASE = "No base service found. Using `PiperService` directly.".freeze
|
20
|
+
NOTE__CUSTOM_BASE = "Custom base service found. Using `%s`.".freeze
|
21
|
+
|
22
|
+
class_option :base_service, aliases: "-b", type: :string
|
23
|
+
class_option :steps, aliases: "-s", type: :numeric, default: 0
|
24
|
+
class_option :verbose, aliases: "-v", type: :boolean, default: true
|
25
|
+
|
26
|
+
def self.source_root
|
27
|
+
File.join(__dir__, "..", "lib", "piper")
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "setup", "Setup config file and a base service."
|
31
|
+
def setup
|
32
|
+
display_version
|
33
|
+
@base_service = (options[:base_service] || DEFAULT_BASE_SERVICE).snakify
|
34
|
+
|
35
|
+
template "templates/#{CONFIG_FILE_NAME}", CONFIG_FILE_NAME
|
36
|
+
template "templates/#{BASE_SERVICE_FILE_NAME}", "app/services/#{base_service_filename}"
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "new [SERVICE_NAME]", "Create a new named service in app/services."
|
40
|
+
def new(name, *attrs)
|
41
|
+
merged_options[:service_name] = name
|
42
|
+
merged_options[:attributes] = attrs
|
43
|
+
|
44
|
+
display_version
|
45
|
+
display_log
|
46
|
+
|
47
|
+
template "templates/#{NAMED_SERVICE_FILE_NAME}", "app/services/#{service_filename}.rb"
|
48
|
+
|
49
|
+
return unless defined?(Bundler)
|
50
|
+
|
51
|
+
build_spec_files || build_test_files
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def attributes
|
57
|
+
@attributes ||= merged_options[:attributes]
|
58
|
+
end
|
59
|
+
|
60
|
+
def step_count
|
61
|
+
@step_count ||= merged_options[:steps]
|
62
|
+
end
|
63
|
+
|
64
|
+
def verbose?
|
65
|
+
@verbose ||= options[:verbose]
|
66
|
+
end
|
67
|
+
|
68
|
+
def config_file_exists?
|
69
|
+
File.exist?(CONFIG_FILE_NAME)
|
70
|
+
end
|
71
|
+
|
72
|
+
def base_service
|
73
|
+
@base_service ||= (merged_options[:base_service] || DEFAULT_PIPER.dup)
|
74
|
+
end
|
75
|
+
|
76
|
+
def base_service_class
|
77
|
+
@base_service_class ||= base_service.servicify.camelify(true)
|
78
|
+
end
|
79
|
+
|
80
|
+
def base_service_filename
|
81
|
+
@base_service_filename ||= (base_service.servicify << ".rb")
|
82
|
+
end
|
83
|
+
|
84
|
+
def service_name
|
85
|
+
@service_name ||= merged_options[:service_name].servicify.camelify(true)
|
86
|
+
end
|
87
|
+
|
88
|
+
def service_filename
|
89
|
+
@service_filename ||= service_name.snakify.servicify
|
90
|
+
end
|
91
|
+
|
92
|
+
def base_service_exists?
|
93
|
+
File.exist?("app/services/#{base_service_filename}")
|
94
|
+
end
|
95
|
+
|
96
|
+
def nondefault_base_service?
|
97
|
+
merged_options[:base_service] != DEFAULT_BASE_SERVICE
|
98
|
+
end
|
99
|
+
|
100
|
+
def merged_options
|
101
|
+
return @merged_options if @merged_options
|
102
|
+
|
103
|
+
@merged_options = if config_file_exists?
|
104
|
+
YAML.load_file(CONFIG_FILE_NAME)
|
105
|
+
.merge(options)
|
106
|
+
else
|
107
|
+
options
|
108
|
+
end.symbolize_keys
|
109
|
+
end
|
110
|
+
|
111
|
+
def display_version
|
112
|
+
say("Piper version #{PiperGem::VERSION}", [:white, :on_blue, :bold]) if verbose?
|
113
|
+
end
|
114
|
+
|
115
|
+
def display_log
|
116
|
+
return unless verbose?
|
117
|
+
|
118
|
+
if config_file_exists?
|
119
|
+
if base_service_exists?
|
120
|
+
say(NOTE__CUSTOM_BASE.dup.gsub(/%s/, base_service_class), :green)
|
121
|
+
else
|
122
|
+
say(NOTE__DEFAULT_BASE, :yellow)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
say(NOTE__NO_CUSTOM_CONF, :yellow)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
PiperRunner.start(ARGV)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module PiperRunnerHelpers
|
2
|
+
module TestHelpers
|
3
|
+
NAMED_SPEC_FILE = "named_service_spec.rb".freeze
|
4
|
+
NAMED_TEST_FILE = "named_service_test.rb".freeze
|
5
|
+
|
6
|
+
def build_spec_files
|
7
|
+
require("rspec/core")
|
8
|
+
say("RSpec detected", :green)
|
9
|
+
template "templates/#{NAMED_SPEC_FILE}", "spec/services/#{service_filename}_spec.rb"
|
10
|
+
true
|
11
|
+
rescue LoadError
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_test_files
|
16
|
+
require("minitest")
|
17
|
+
say("Minitest detected", :green)
|
18
|
+
template "templates/#{NAMED_TEST_FILE}", "test/services/#{service_filename}_test.rb"
|
19
|
+
true
|
20
|
+
rescue LoadError
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module PiperDSL
|
2
|
+
module PiperHelpers
|
3
|
+
FAIL__NO_BLOCK = "Expected a block. None given.".freeze
|
4
|
+
|
5
|
+
def bump(attribute)
|
6
|
+
raise ArgumentError, FAIL__NO_BLOCK unless block_given?
|
7
|
+
|
8
|
+
attributes[attribute] = yield
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
return @__error_message unless block_given?
|
13
|
+
|
14
|
+
@__error_message = yield
|
15
|
+
end
|
16
|
+
alias fail_message message
|
17
|
+
alias mssg message
|
18
|
+
|
19
|
+
def result_object
|
20
|
+
return @__result_object unless block_given?
|
21
|
+
|
22
|
+
@__result_object = yield
|
23
|
+
end
|
24
|
+
alias object result_object
|
25
|
+
alias rslt result_object
|
26
|
+
|
27
|
+
def fail_object
|
28
|
+
return @__fail_object unless block_given?
|
29
|
+
|
30
|
+
@__fail_object = yield
|
31
|
+
end
|
32
|
+
alias fobj fail_object
|
33
|
+
|
34
|
+
def condition
|
35
|
+
return @__condition unless block_given?
|
36
|
+
|
37
|
+
@__condition = yield
|
38
|
+
end
|
39
|
+
alias cond condition
|
40
|
+
|
41
|
+
def condition_manual?
|
42
|
+
!defined?(@__condition).nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
def last_result=(val)
|
46
|
+
@__last_result = val
|
47
|
+
end
|
48
|
+
|
49
|
+
def last_result
|
50
|
+
@__last_result
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module PiperDSL
|
2
|
+
class Pipe
|
3
|
+
include ::Dry::Monads[:result]
|
4
|
+
|
5
|
+
attr_reader :service
|
6
|
+
|
7
|
+
def initialize(step_name, &block)
|
8
|
+
@step_name = step_name
|
9
|
+
@block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def perform(service, last_result = nil)
|
13
|
+
@service = service
|
14
|
+
service.last_result = last_result
|
15
|
+
result = service.instance_eval(&@block)
|
16
|
+
condition = service.condition_manual? ? service.cond : result
|
17
|
+
|
18
|
+
@result_object = service.object || result
|
19
|
+
@fail_object = service.fail_object || service.object || result
|
20
|
+
|
21
|
+
return condition if condition.is_a? Dry::Monads::Result
|
22
|
+
|
23
|
+
prepare_response!(condition)
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def prepare_response!(condition)
|
29
|
+
if condition
|
30
|
+
Success(@result_object || true)
|
31
|
+
else
|
32
|
+
failure_object = { service: service, object: @fail_object, message: service.message }
|
33
|
+
failure_object[:step] = @step_name if service.class.debug_steps?
|
34
|
+
|
35
|
+
Failure(failure_object)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module PiperDSL
|
2
|
+
module PiperSteps
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def service_steps
|
6
|
+
@service_steps || @service_steps = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def debug_steps
|
10
|
+
@__debug_steps = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def debug_steps?
|
14
|
+
!@__debug_steps.nil?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.included(base)
|
19
|
+
base.extend(ClassMethods)
|
20
|
+
end
|
21
|
+
|
22
|
+
def debug_steps?
|
23
|
+
self.class.debug_steps?
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def perform_steps
|
29
|
+
klass = self.class
|
30
|
+
|
31
|
+
return Success(true) if klass.service_steps.empty?
|
32
|
+
|
33
|
+
arr = klass.service_steps.dup
|
34
|
+
step = arr.shift
|
35
|
+
|
36
|
+
result = step.perform(self)
|
37
|
+
result = result.bind(proc_step(arr.shift)) until arr.empty?
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def proc_step(step)
|
42
|
+
->(result) { step.perform(self, result) }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "dry-monads"
|
2
|
+
require "dry-struct"
|
3
|
+
require "dry-types"
|
4
|
+
|
5
|
+
require_relative "./dsl/piper_helpers"
|
6
|
+
require_relative "./dsl/piper_pipe"
|
7
|
+
require_relative "./dsl/piper_steps"
|
8
|
+
|
9
|
+
class PiperService < Dry::Struct
|
10
|
+
include PiperDSL::PiperSteps
|
11
|
+
include PiperDSL::PiperHelpers
|
12
|
+
include Dry::Monads[:result]
|
13
|
+
|
14
|
+
module Types
|
15
|
+
include Dry.Types()
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(_)
|
19
|
+
raise NotImplementedError unless self.class < PiperService
|
20
|
+
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def call
|
25
|
+
result = nil
|
26
|
+
|
27
|
+
if defined? ActiveRecord::Base
|
28
|
+
ActiveRecord::Base.transaction do
|
29
|
+
result = perform_steps
|
30
|
+
|
31
|
+
raise ActiveRecord::Rollback if result.failure?
|
32
|
+
end
|
33
|
+
else
|
34
|
+
result = perform_steps
|
35
|
+
end
|
36
|
+
|
37
|
+
result
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.pipe(desc, &block)
|
41
|
+
raise ArgumentError, "missing block" unless block_given?
|
42
|
+
|
43
|
+
pipepart = PiperDSL::Pipe.new(desc, &block)
|
44
|
+
|
45
|
+
service_steps << pipepart
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# module Piper
|
2
|
+
# module RunnerHelpers
|
3
|
+
# def config_file_exists?
|
4
|
+
# File.exist?(CONFIG_FILE_NAME)
|
5
|
+
# end
|
6
|
+
|
7
|
+
# def base_service
|
8
|
+
# @base_service ||= (merged_options[:base_service] || DEFAULT_BASE_DRY_SERVICE).downcase
|
9
|
+
# end
|
10
|
+
|
11
|
+
# def base_service_filename
|
12
|
+
# @base_service_filename ||= (base_service.servicify << ".rb")
|
13
|
+
# end
|
14
|
+
|
15
|
+
# def base_service_exists?
|
16
|
+
# File.exist?("app/services/#{base_service_filename}")
|
17
|
+
# end
|
18
|
+
|
19
|
+
# def nondefault_base_service?
|
20
|
+
# merged_options[:base_service] != DEFAULT_DRY
|
21
|
+
# end
|
22
|
+
|
23
|
+
# def merged_options
|
24
|
+
# return options unless config_file_exists?
|
25
|
+
|
26
|
+
# YAML.load_file(CONFIG_FILE_NAME)
|
27
|
+
# .merge(options)
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class String
|
2
|
+
def servicify
|
3
|
+
snaked_self = snakify
|
4
|
+
return snaked_self if snaked_self.end_with?("_service")
|
5
|
+
|
6
|
+
snaked_self << "_service"
|
7
|
+
end
|
8
|
+
|
9
|
+
def camelify(classify = false)
|
10
|
+
if classify
|
11
|
+
capitalize
|
12
|
+
.gsub(%r{\/(\w)}) { "::" << Regexp.last_match(1).upcase }
|
13
|
+
.gsub(/_(\w)/) { Regexp.last_match(1).upcase }
|
14
|
+
else
|
15
|
+
split("_").collect(&:capitalize).join
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def snakify
|
20
|
+
gsub(/:{1,2}/, "/")
|
21
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, "\\1_\\2")
|
22
|
+
.gsub(/([a-z\d])([A-Z])/, "\\1_\\2")
|
23
|
+
.tr("-", "_")
|
24
|
+
.downcase
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= service_name %> < <%= base_service_class %>
|
4
|
+
<%- attributes.each do |attr| -%>
|
5
|
+
attribute :<%= attr %>, Types::Any
|
6
|
+
<%- end -%>
|
7
|
+
<%- step_count.times do |step_number| %>
|
8
|
+
pipe :step_<%= step_number + 1 %> do
|
9
|
+
true
|
10
|
+
end
|
11
|
+
<%- end -%>
|
12
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "rails_helper"
|
2
|
+
|
3
|
+
describe <%= service_name %> do
|
4
|
+
subject { service.call }
|
5
|
+
|
6
|
+
<%- if attributes.any? -%>
|
7
|
+
let(:service) { described_class.new(params) }
|
8
|
+
|
9
|
+
<%- if attributes.size > 1 -%>
|
10
|
+
let(:params) do
|
11
|
+
{
|
12
|
+
<%- attributes.map do |atr| -%>
|
13
|
+
<%= atr %>: "1",
|
14
|
+
<%- end -%>
|
15
|
+
}
|
16
|
+
end
|
17
|
+
<%- else -%>
|
18
|
+
let(:params) { { <%=attributes.first%>: "1" } }
|
19
|
+
<%- end -%>
|
20
|
+
|
21
|
+
context "NO arguments passed" do
|
22
|
+
subject { described_class.new }
|
23
|
+
|
24
|
+
it "Raises DryStruct`s argument error" do
|
25
|
+
expect { subject }.to raise_error(Dry::Struct::Error)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "arguments passed" do
|
30
|
+
it "is a success" do
|
31
|
+
expect(subject).to be_success
|
32
|
+
end
|
33
|
+
end
|
34
|
+
<%- else -%>
|
35
|
+
context "no arguments passed" do
|
36
|
+
it "is a success" do
|
37
|
+
expect(subject).to be_success
|
38
|
+
end
|
39
|
+
end
|
40
|
+
<%- end -%>
|
41
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class <%= service_name %>Test < Minitest::Test
|
4
|
+
<%- if attributes.any? -%>
|
5
|
+
def setup
|
6
|
+
@subject = <%= service_name %>.new(<%= attributes.map {|a| "#{a}: 1" }.join(", ") %>)
|
7
|
+
end
|
8
|
+
<% else %>
|
9
|
+
def setup
|
10
|
+
@subject = <%= service_name %>.new
|
11
|
+
end
|
12
|
+
<% end -%>
|
13
|
+
|
14
|
+
def test_call
|
15
|
+
assert @subject.call.success?
|
16
|
+
end
|
17
|
+
end
|
data/lib/piper-rb.rb
ADDED
data/piper-rb.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "piper/version"
|
4
|
+
|
5
|
+
ERR_RG_2_REQUIRED = "RubyGems 2.0 or newer is required to protect against public gem pushes.".freeze
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
# RubyGems < 2.0 does not allow to specify metadata. We do not want to use RubyGems < 2.0
|
9
|
+
raise ERR_RG_2_REQUIRED unless spec.respond_to?(:metadata)
|
10
|
+
|
11
|
+
spec.name = "piper-rb"
|
12
|
+
spec.version = PiperGem::VERSION.dup
|
13
|
+
spec.date = "2019-12-07"
|
14
|
+
spec.summary = ""
|
15
|
+
spec.authors = ["Jakub Żuchowski"]
|
16
|
+
spec.email = "ellmo@ellmo.net"
|
17
|
+
spec.homepage = "https://github.com/ellmo/piper-rb"
|
18
|
+
spec.license = "MIT"
|
19
|
+
# Fetch all git-versioned files, excluding all the default test locations.
|
20
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
21
|
+
f.match(%r{^(test|spec|features)/})
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.executables = "piper"
|
25
|
+
|
26
|
+
spec.required_ruby_version = ">= 2.5.0"
|
27
|
+
|
28
|
+
spec.add_dependency "dry-monads", "~> 1.3"
|
29
|
+
spec.add_dependency "dry-struct", "~> 1.1"
|
30
|
+
spec.add_dependency "dry-types", "~> 1.2"
|
31
|
+
spec.add_dependency "thor", ">= 0.19", "<= 2.0"
|
32
|
+
|
33
|
+
spec.add_development_dependency "rspec", "~> 3.9"
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: piper-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jakub Żuchowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-12-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: dry-monads
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-struct
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-types
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.2'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: thor
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.19'
|
62
|
+
- - "<="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '2.0'
|
65
|
+
type: :runtime
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0.19'
|
72
|
+
- - "<="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '2.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rspec
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3.9'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.9'
|
89
|
+
description:
|
90
|
+
email: ellmo@ellmo.net
|
91
|
+
executables:
|
92
|
+
- piper
|
93
|
+
extensions: []
|
94
|
+
extra_rdoc_files: []
|
95
|
+
files:
|
96
|
+
- ".github/workflows/ruby.yml"
|
97
|
+
- ".gitignore"
|
98
|
+
- ".rspec"
|
99
|
+
- ".rubocop.yml"
|
100
|
+
- Gemfile
|
101
|
+
- README.md
|
102
|
+
- bin/piper
|
103
|
+
- bin/runner/test_helpers.rb
|
104
|
+
- lib/piper-rb.rb
|
105
|
+
- lib/piper/dsl/piper_helpers.rb
|
106
|
+
- lib/piper/dsl/piper_pipe.rb
|
107
|
+
- lib/piper/dsl/piper_steps.rb
|
108
|
+
- lib/piper/hash_helpers.rb
|
109
|
+
- lib/piper/piper_service.rb
|
110
|
+
- lib/piper/runner_helpers.rb
|
111
|
+
- lib/piper/string_helpers.rb
|
112
|
+
- lib/piper/templates/.piper.yml.tt
|
113
|
+
- lib/piper/templates/base_service.rb.tt
|
114
|
+
- lib/piper/templates/named_service.rb.tt
|
115
|
+
- lib/piper/templates/named_service_spec.rb.tt
|
116
|
+
- lib/piper/templates/named_service_test.rb.tt
|
117
|
+
- lib/piper/version.rb
|
118
|
+
- piper-rb.gemspec
|
119
|
+
homepage: https://github.com/ellmo/piper-rb
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 2.5.0
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubygems_version: 3.0.3
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: ''
|
142
|
+
test_files: []
|