light-services 0.5.4 → 2.0.0.beta1
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 +5 -5
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci.yml +63 -0
- data/.gitignore +3 -4
- data/.rspec +1 -0
- data/.rubocop.yml +22 -8
- data/CHANGELOG.md +1 -0
- data/CODE_OF_CONDUCT.md +55 -30
- data/Gemfile +16 -2
- data/Gemfile.lock +101 -0
- data/LICENSE.txt +1 -1
- data/README.md +3 -149
- data/Rakefile +2 -2
- data/bin/console +4 -4
- data/lib/light/services.rb +4 -7
- data/lib/light/services/base.rb +127 -27
- data/lib/light/services/base_with_context.rb +32 -0
- data/lib/light/services/class_based_collection/base.rb +88 -0
- data/lib/light/services/class_based_collection/mount.rb +33 -0
- data/lib/light/services/collection/arguments.rb +34 -0
- data/lib/light/services/collection/base.rb +47 -0
- data/lib/light/services/collection/outputs.rb +16 -0
- data/lib/light/services/config.rb +63 -0
- data/lib/light/services/exceptions.rb +3 -2
- data/lib/light/services/messages.rb +77 -34
- data/lib/light/services/settings/argument.rb +50 -0
- data/lib/light/services/settings/output.rb +34 -0
- data/lib/light/services/settings/step.rb +62 -0
- data/lib/light/services/version.rb +1 -1
- data/light-services.gemspec +21 -24
- metadata +34 -127
- data/.codeclimate.yml +0 -16
- data/.ruby-gemset +0 -1
- data/.travis.yml +0 -29
- data/Appraisals +0 -21
- data/gemfiles/rails_4_0.gemfile +0 -7
- data/gemfiles/rails_4_0.gemfile.lock +0 -115
- data/gemfiles/rails_4_1.gemfile +0 -7
- data/gemfiles/rails_4_1.gemfile.lock +0 -119
- data/gemfiles/rails_4_2.gemfile +0 -7
- data/gemfiles/rails_4_2.gemfile.lock +0 -146
- data/gemfiles/rails_5_0.gemfile +0 -7
- data/gemfiles/rails_5_0.gemfile.lock +0 -150
- data/gemfiles/rails_5_1.gemfile +0 -7
- data/gemfiles/rails_5_1.gemfile.lock +0 -150
- data/lib/light/services/callbacks.rb +0 -54
- data/lib/light/services/outputs.rb +0 -70
- data/lib/light/services/parameters.rb +0 -96
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Light
|
4
|
+
module Services
|
5
|
+
class << self
|
6
|
+
def configure
|
7
|
+
yield config
|
8
|
+
end
|
9
|
+
|
10
|
+
def config
|
11
|
+
@config ||= Config.new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Config
|
16
|
+
# Constants
|
17
|
+
DEFAULTS = {
|
18
|
+
use_transactions: true,
|
19
|
+
|
20
|
+
load_errors: true,
|
21
|
+
break_on_error: true,
|
22
|
+
raise_on_error: false,
|
23
|
+
rollback_on_error: true,
|
24
|
+
|
25
|
+
load_warnings: true,
|
26
|
+
break_on_warning: false,
|
27
|
+
raise_on_warning: false,
|
28
|
+
rollback_on_warning: false
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
# Getters / Setters
|
32
|
+
attr_accessor :use_transactions,
|
33
|
+
:load_errors, :break_on_error, :raise_on_error, :rollback_on_error,
|
34
|
+
:load_warnings, :break_on_warning, :raise_on_warning, :rollback_on_warning
|
35
|
+
|
36
|
+
def initialize
|
37
|
+
reset_to_defaults!
|
38
|
+
end
|
39
|
+
|
40
|
+
def set(key, value)
|
41
|
+
instance_variable_set("@#{key}", value)
|
42
|
+
end
|
43
|
+
|
44
|
+
def get(key)
|
45
|
+
instance_variable_get("@#{key}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def reset_to_defaults!
|
49
|
+
DEFAULTS.each do |key, value|
|
50
|
+
set(key, value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_h
|
55
|
+
DEFAULTS.keys.map { |key| [key, get(key)] }.to_h
|
56
|
+
end
|
57
|
+
|
58
|
+
def merge(config)
|
59
|
+
to_h.merge(config)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module Light
|
4
4
|
module Services
|
5
5
|
class Error < StandardError; end
|
6
|
-
class
|
7
|
-
class
|
6
|
+
class ArgTypeError < Error; end
|
7
|
+
class NoStepError < Error; end
|
8
|
+
class TwoConditions < Error; end
|
8
9
|
end
|
9
10
|
end
|
@@ -1,65 +1,108 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This class stores errors and warnings
|
3
4
|
module Light
|
4
5
|
module Services
|
5
6
|
class Messages
|
6
|
-
def initialize
|
7
|
-
@
|
7
|
+
def initialize(config)
|
8
|
+
@break = false
|
9
|
+
@config = config
|
10
|
+
@messages = {}
|
8
11
|
end
|
9
12
|
|
10
|
-
def add(key, message)
|
11
|
-
|
12
|
-
storage[key] << message
|
13
|
-
end
|
13
|
+
def add(key, message, opts = {})
|
14
|
+
@messages[key] ||= []
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
if message.is_a?(Array)
|
17
|
+
@messages[key] += message
|
18
|
+
else
|
19
|
+
@messages[key] << message
|
18
20
|
end
|
19
|
-
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
22
|
+
raise!(key, message)
|
23
|
+
break!(opts[:break])
|
24
|
+
rollback!(opts[:rollback])
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
27
|
+
def break?
|
28
|
+
@break
|
29
29
|
end
|
30
30
|
|
31
|
-
def
|
32
|
-
|
31
|
+
def copy_from(entity, opts = {})
|
32
|
+
if defined?(ActiveRecord::Base) && entity.is_a?(ActiveRecord::Base)
|
33
|
+
copy_from(entity.errors.messages, opts)
|
34
|
+
elsif entity.respond_to?(:each)
|
35
|
+
entity.each do |key, message|
|
36
|
+
add(key, message, opts)
|
37
|
+
end
|
38
|
+
else
|
39
|
+
# TODO: Update error
|
40
|
+
raise Light::Services::Error
|
41
|
+
end
|
33
42
|
end
|
34
43
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
44
|
+
def copy_to(entity)
|
45
|
+
if defined?(ActiveRecord::Base) && entity.is_a?(ActiveRecord::Base)
|
46
|
+
each do |key, message|
|
47
|
+
entity.errors.add(key, message)
|
48
|
+
end
|
49
|
+
elsif entity.is_a?(Hash)
|
50
|
+
each do |key, message|
|
51
|
+
entity[key] ||= []
|
52
|
+
entity[key] << message
|
53
|
+
end
|
54
|
+
else
|
55
|
+
# TODO: Update error
|
56
|
+
raise Light::Services::Error
|
57
|
+
end
|
38
58
|
|
39
|
-
|
40
|
-
storage
|
59
|
+
entity
|
41
60
|
end
|
42
61
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
62
|
+
def errors_to_record(record)
|
63
|
+
if !defined?(ActiveRecord::Base) || !record.is_a?(ActiveRecord::Base)
|
64
|
+
# TODO: Update error
|
65
|
+
raise Light::Services::Error
|
66
|
+
end
|
67
|
+
|
68
|
+
errors.each do |key, message|
|
69
|
+
record.errors.add(key, message)
|
48
70
|
end
|
71
|
+
|
72
|
+
record
|
49
73
|
end
|
50
74
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
75
|
+
def method_missing(method, *args, &block)
|
76
|
+
if @messages.respond_to?(method)
|
77
|
+
@messages.public_send(method, *args, &block)
|
78
|
+
else
|
79
|
+
super
|
54
80
|
end
|
55
81
|
end
|
56
82
|
|
57
|
-
|
83
|
+
def respond_to_missing?(method, include_private = false)
|
84
|
+
@messages.respond_to?(method, include_private) || super
|
85
|
+
end
|
58
86
|
|
59
87
|
private
|
60
88
|
|
61
|
-
|
62
|
-
|
89
|
+
def break!(break_execution)
|
90
|
+
return unless break_execution.nil? ? @config[:break_on_add] : break_execution
|
91
|
+
|
92
|
+
@break = true
|
93
|
+
end
|
94
|
+
|
95
|
+
def raise!(key, message)
|
96
|
+
return unless @config[:raise_on_add]
|
97
|
+
|
98
|
+
raise Light::Services::Error, "#{key.to_s.capitalize} #{message}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def rollback!(rollback)
|
102
|
+
return if !defined?(ActiveRecord::Rollback) || !(rollback.nil? ? @config[:rollback_on_add] : rollback)
|
103
|
+
|
104
|
+
raise ActiveRecord::Rollback
|
105
|
+
end
|
63
106
|
end
|
64
107
|
end
|
65
108
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class defines settings for argument
|
4
|
+
module Light
|
5
|
+
module Services
|
6
|
+
module Settings
|
7
|
+
class Argument
|
8
|
+
# Getters
|
9
|
+
attr_reader :name, :default_exists, :default, :context, :optional
|
10
|
+
|
11
|
+
def initialize(name, service_class, opts = {})
|
12
|
+
@name = name
|
13
|
+
@service_class = service_class
|
14
|
+
|
15
|
+
@type = opts.delete(:type)
|
16
|
+
@context = opts.delete(:context)
|
17
|
+
@default_exists = opts.key?(:default)
|
18
|
+
@default = opts.delete(:default)
|
19
|
+
@optional = opts.delete(:optional)
|
20
|
+
|
21
|
+
define_methods
|
22
|
+
end
|
23
|
+
|
24
|
+
def valid_type?(value)
|
25
|
+
return if !@type || [*@type].any? do |type|
|
26
|
+
if type == :boolean
|
27
|
+
value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
28
|
+
else
|
29
|
+
value.is_a?(type)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
raise Light::Services::ArgTypeError, "#{@service_class} argument `#{name}` must be " \
|
34
|
+
"a #{[*@type].join(', ')} (currently: #{value.class})"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def define_methods
|
40
|
+
name = @name
|
41
|
+
|
42
|
+
@service_class.define_method(@name) { @arguments.get(name) }
|
43
|
+
@service_class.define_method("#{@name}?") { !!@arguments.get(name) } # rubocop:disable Style/DoubleNegation
|
44
|
+
@service_class.define_method("#{@name}=") { |value| @arguments.set(name, value) }
|
45
|
+
@service_class.send(:private, "#{@name}=")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class defines settings for output
|
4
|
+
module Light
|
5
|
+
module Services
|
6
|
+
module Settings
|
7
|
+
class Output
|
8
|
+
# Getters
|
9
|
+
attr_reader :name, :default_exists, :default
|
10
|
+
|
11
|
+
def initialize(name, service_class, opts = {})
|
12
|
+
@name = name
|
13
|
+
@service_class = service_class
|
14
|
+
|
15
|
+
@default_exists = opts.key?(:default)
|
16
|
+
@default = opts.delete(:default)
|
17
|
+
|
18
|
+
define_methods
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def define_methods
|
24
|
+
name = @name
|
25
|
+
|
26
|
+
@service_class.define_method(@name) { @outputs.get(name) }
|
27
|
+
@service_class.define_method("#{@name}?") { !!@outputs.get(name) } # rubocop:disable Style/DoubleNegation
|
28
|
+
@service_class.define_method("#{@name}=") { |value| @outputs.set(name, value) }
|
29
|
+
@service_class.send(:private, "#{@name}=")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class defines settings for step
|
4
|
+
module Light
|
5
|
+
module Services
|
6
|
+
module Settings
|
7
|
+
class Step
|
8
|
+
# Getters
|
9
|
+
attr_reader :name, :always
|
10
|
+
|
11
|
+
def initialize(name, service_class, opts = {})
|
12
|
+
@name = name
|
13
|
+
@service_class = service_class
|
14
|
+
|
15
|
+
@if = opts[:if]
|
16
|
+
@unless = opts[:unless]
|
17
|
+
@always = opts[:always]
|
18
|
+
|
19
|
+
if @if && @unless
|
20
|
+
raise Light::Services::TwoConditions, "#{service_class} `if` and `unless` cannot be specified " \
|
21
|
+
"for the step `#{name}` at the same time"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(instance)
|
26
|
+
return false unless run?(instance)
|
27
|
+
|
28
|
+
if instance.respond_to?(name, true)
|
29
|
+
instance.send(name)
|
30
|
+
true
|
31
|
+
else
|
32
|
+
raise Light::Services::NoStepError, "Cannot find step `#{name}` in service `#{@service_class}`"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def run?(instance)
|
39
|
+
if @if
|
40
|
+
check_condition(@if, instance)
|
41
|
+
elsif @unless
|
42
|
+
!check_condition(@unless, instance)
|
43
|
+
else
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_condition(condition, instance)
|
49
|
+
case condition
|
50
|
+
when Symbol
|
51
|
+
instance.public_send(condition)
|
52
|
+
when Proc
|
53
|
+
condition.call
|
54
|
+
else
|
55
|
+
raise Light::Services::Error, "#{@service_class} condition should be a Symbol or Proc " \
|
56
|
+
"for the step `#{@name}` (currently: #{condition.class})"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/light-services.gemspec
CHANGED
@@ -1,35 +1,32 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
-
require 'light/services/version'
|
3
|
+
require_relative "lib/light/services/version"
|
7
4
|
|
8
5
|
Gem::Specification.new do |spec|
|
9
|
-
spec.name =
|
6
|
+
spec.name = "light-services"
|
10
7
|
spec.version = Light::Services::VERSION
|
11
|
-
spec.authors = [
|
12
|
-
spec.email = [
|
8
|
+
spec.authors = ["Andrew Emelianenko"]
|
9
|
+
spec.email = ["emelianenko.web@gmail.com"]
|
13
10
|
|
14
|
-
spec.summary =
|
15
|
-
spec.description =
|
16
|
-
spec.homepage =
|
17
|
-
spec.license =
|
11
|
+
spec.summary = "Powerful implementation of Service Object pattern for Ruby and Rails"
|
12
|
+
spec.description = "Powerful implementation of Service Object pattern for Ruby and Rails"
|
13
|
+
spec.homepage = "https://github.com/light-ruby/light-services"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
18
16
|
|
19
|
-
spec.
|
20
|
-
.split("\x0")
|
21
|
-
.reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
22
18
|
|
23
|
-
spec.
|
24
|
-
spec.
|
25
|
-
spec.
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/light-ruby/light-services"
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/light-ruby/light-services/blob/master/CHANGELOG.md"
|
26
22
|
|
27
|
-
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
26
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
|
+
end
|
28
28
|
|
29
|
-
spec.
|
30
|
-
spec.
|
31
|
-
spec.
|
32
|
-
spec.add_development_dependency 'rspec', '~> 3.0'
|
33
|
-
spec.add_development_dependency 'simplecov', '~> 0.11.2'
|
34
|
-
spec.add_development_dependency 'codeclimate-test-reporter'
|
29
|
+
spec.bindir = "exe"
|
30
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
|
+
spec.require_paths = ["lib"]
|
35
32
|
end
|