light-services 0.6.3 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/config/rubocop_linter_action.yml +4 -0
  3. data/.github/workflows/ci.yml +63 -0
  4. data/.gitignore +3 -4
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +22 -8
  7. data/CHANGELOG.md +1 -0
  8. data/CODE_OF_CONDUCT.md +55 -30
  9. data/Gemfile +16 -2
  10. data/Gemfile.lock +101 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +3 -149
  13. data/Rakefile +2 -2
  14. data/bin/console +4 -4
  15. data/lib/light/services.rb +4 -7
  16. data/lib/light/services/base.rb +121 -31
  17. data/lib/light/services/base_with_context.rb +32 -0
  18. data/lib/light/services/class_based_collection/base.rb +88 -0
  19. data/lib/light/services/class_based_collection/mount.rb +33 -0
  20. data/lib/light/services/collection/arguments.rb +34 -0
  21. data/lib/light/services/collection/base.rb +47 -0
  22. data/lib/light/services/collection/outputs.rb +16 -0
  23. data/lib/light/services/config.rb +63 -0
  24. data/lib/light/services/exceptions.rb +3 -2
  25. data/lib/light/services/messages.rb +72 -45
  26. data/lib/light/services/settings/argument.rb +50 -0
  27. data/lib/light/services/settings/output.rb +34 -0
  28. data/lib/light/services/settings/step.rb +62 -0
  29. data/lib/light/services/version.rb +1 -1
  30. data/light-services.gemspec +21 -25
  31. metadata +34 -122
  32. data/.codeclimate.yml +0 -18
  33. data/.ruby-gemset +0 -1
  34. data/.ruby-version +0 -1
  35. data/.travis.yml +0 -33
  36. data/Appraisals +0 -13
  37. data/gemfiles/rails_5_0.gemfile +0 -7
  38. data/gemfiles/rails_5_0.gemfile.lock +0 -151
  39. data/gemfiles/rails_5_1.gemfile +0 -7
  40. data/gemfiles/rails_5_1.gemfile.lock +0 -151
  41. data/gemfiles/rails_5_2.gemfile +0 -7
  42. data/gemfiles/rails_5_2.gemfile.lock +0 -159
  43. data/lib/light/services/callbacks.rb +0 -54
  44. data/lib/light/services/outputs.rb +0 -70
  45. data/lib/light/services/parameters.rb +0 -96
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Collection to store outputs values
4
+ module Light
5
+ module Services
6
+ module Collection
7
+ class Outputs < Base
8
+ private
9
+
10
+ def settings_collection
11
+ @instance.class.outputs
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -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 ParamRequired < Light::Services::Error; end
7
- class ParamType < Light::Services::Error; end
6
+ class ArgTypeError < Error; end
7
+ class NoStepError < Error; end
8
+ class TwoConditions < Error; end
8
9
  end
9
10
  end
@@ -1,80 +1,107 @@
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
- @storage = {}
7
+ def initialize(config)
8
+ @break = false
9
+ @config = config
10
+ @messages = {}
8
11
  end
9
12
 
10
- def add(key, message, rollback: true)
11
- storage[key] ||= []
12
- storage[key] << message
13
+ def add(key, message, opts = {})
14
+ @messages[key] ||= []
13
15
 
14
- rollback! if rollback
16
+ if message.is_a?(Array)
17
+ @messages[key] += message
18
+ else
19
+ @messages[key] << message
20
+ end
21
+
22
+ raise!(key, message)
23
+ break!(opts[:break])
24
+ rollback!(opts[:rollback])
15
25
  end
16
26
 
17
- def from_record(record, rollback: true)
18
- return unless record.errors.any?
27
+ def break?
28
+ @break
29
+ end
19
30
 
20
- record.errors.messages.each do |key, messages|
21
- messages.each do |message|
22
- add(key, message, rollback: false)
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)
23
37
  end
38
+ else
39
+ # TODO: Update error
40
+ raise Light::Services::Error
24
41
  end
25
-
26
- rollback! if rollback
27
42
  end
28
43
 
29
- def from_service(service, rollback: true)
30
- return unless service.errors.any?
31
-
32
- service.errors.each do |key, message|
33
- add(key, message, rollback: false)
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
34
57
  end
35
58
 
36
- rollback! if rollback
59
+ entity
37
60
  end
38
61
 
39
- def delete(key)
40
- storage.delete(key)
41
- end
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
42
67
 
43
- def blank?
44
- storage.empty?
45
- end
68
+ errors.each do |key, message|
69
+ record.errors.add(key, message)
70
+ end
46
71
 
47
- def any?
48
- !blank?
72
+ record
49
73
  end
50
74
 
51
- def to_hash
52
- storage
75
+ def method_missing(method, *args, &block)
76
+ if @messages.respond_to?(method)
77
+ @messages.public_send(method, *args, &block)
78
+ else
79
+ super
80
+ end
53
81
  end
54
82
 
55
- def flatten
56
- to_hash.flat_map do |key, messages|
57
- messages.map do |message|
58
- [key, message]
59
- end
60
- end
83
+ def respond_to_missing?(method, include_private = false)
84
+ @messages.respond_to?(method, include_private) || super
61
85
  end
62
86
 
63
- def each
64
- flatten.each do |key, message|
65
- yield key, message
66
- end
87
+ private
88
+
89
+ def break!(break_execution)
90
+ return unless break_execution.nil? ? @config[:break_on_add] : break_execution
91
+
92
+ @break = true
67
93
  end
68
94
 
69
- alias to_h to_hash
95
+ def raise!(key, message)
96
+ return unless @config[:raise_on_add]
70
97
 
71
- private
98
+ raise Light::Services::Error, "#{key.to_s.capitalize} #{message}"
99
+ end
72
100
 
73
- # Getters / Setters
74
- attr_accessor :storage
101
+ def rollback!(rollback)
102
+ return if !defined?(ActiveRecord::Rollback) || !(rollback.nil? ? @config[:rollback_on_add] : rollback)
75
103
 
76
- def rollback!
77
- raise ActiveRecord::Rollback if defined?(ActiveRecord::Rollback)
104
+ raise ActiveRecord::Rollback
78
105
  end
79
106
  end
80
107
  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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Light
4
4
  module Services
5
- VERSION = '0.6.3'
5
+ VERSION = "2.0.0.beta1"
6
6
  end
7
7
  end
@@ -1,36 +1,32 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- lib = File.expand_path('../lib', __FILE__)
5
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
-
7
- require 'light/services/version'
3
+ require_relative "lib/light/services/version"
8
4
 
9
5
  Gem::Specification.new do |spec|
10
- spec.name = 'light-services'
6
+ spec.name = "light-services"
11
7
  spec.version = Light::Services::VERSION
12
- spec.authors = ['Andrew Emelianenko']
13
- spec.email = ['emelianenko.web@gmail.com']
8
+ spec.authors = ["Andrew Emelianenko"]
9
+ spec.email = ["emelianenko.web@gmail.com"]
14
10
 
15
- spec.summary = 'Light pattern Services Object for Ruby/Rails'
16
- spec.description = 'Light pattern Services Object for Ruby/Rails from Light Ruby'
17
- spec.homepage = 'https://github.com/light-ruby/light-services'
18
- spec.license = 'MIT'
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")
19
16
 
20
- spec.files = `git ls-files -z`
21
- .split("\x0")
22
- .reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
23
18
 
24
- spec.bindir = 'exe'
25
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
- spec.require_paths = ['lib']
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"
27
22
 
28
- spec.add_dependency 'rails', '> 5.0'
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
29
28
 
30
- spec.add_development_dependency 'bundler', '~> 2.0'
31
- spec.add_development_dependency 'rake', '~> 10.0'
32
- spec.add_development_dependency 'appraisal', '~> 2.1'
33
- spec.add_development_dependency 'rspec', '~> 3.0'
34
- spec.add_development_dependency 'simplecov', '~> 0.11.2'
35
- 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"]
36
32
  end