boxenn 3.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fbc20a9c92ec4119ce2cf95528c76cd9b63a182f17a2681255e217ff626ae64a
4
+ data.tar.gz: 6e81cb746c69f65167d31a7bd488667a32b0afe46351be116ac43651885d45ea
5
+ SHA512:
6
+ metadata.gz: 16299c51f0c34f72bf2301f4ff875e81ee3aef50491fc807fdb8107dad4982973054fd7ac531f3428b0c61852e3a46a9012d3861153ec0e7fbae89dc984848cb
7
+ data.tar.gz: 346fbe465196695f5395910d7f8166ec095f3893f4349bf36571553429461c5bc2b979f25c6c998cbacd13b5fe441375e4ea3df9a3dade7951b2eea0c3c7ea38
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 sunnyfounder
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # Boxenn
2
+
3
+ ![Gem Version](https://badge.fury.io/rb/boxenn.svg)
4
+ ![CI Status](https://github.com/sunnyfounder/boxenn/actions/workflows/ci.yml/badge.svg)
5
+ [![codecov](https://codecov.io/gh/sunnyfounder/boxenn/branch/main/graph/badge.svg?token=IC8NGTNYIZ)](https://codecov.io/gh/sunnyfounder/boxenn)
6
+
7
+ A DDD-oriented infrastructure for ruby/rails projects based on the dry-rb ecosystem.
8
+
9
+ ## Installation
10
+ Add this line to your application's Gemfile\
11
+ `gem 'boxenn'`\
12
+ And then execute:\
13
+ `$ bundle install`
14
+
15
+ ## Examples
16
+ * [Working With Sidekiq](examples/sidekiq/README.md)
17
+
18
+ ## License
19
+
20
+ See `LICENSE` file.
data/boxenn.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'boxenn/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'boxenn'
7
+ spec.version = Boxenn::VERSION.dup
8
+ spec.license = 'MIT'
9
+ spec.summary = 'A ddd-oriented infrastructure for ruby/rails projects based on the dry-rb ecosystem.'
10
+ spec.authors = ['Michael Fu', 'Oscar', 'Joseph']
11
+ spec.email = ['oscarada87@gmail.com']
12
+ spec.files = Dir['CHANGELOG.md', 'LICENSE', 'README.md', 'boxenn.gemspec', 'lib/**/*']
13
+ spec.homepage = 'https://github.com/sunnyfounder/boxenn'
14
+ spec.required_ruby_version = '>= 2.6.0'
15
+ spec.require_paths = ['lib']
16
+
17
+ spec.add_runtime_dependency 'dry-initializer', '~> 3.0'
18
+ spec.add_runtime_dependency 'dry-monads', '~> 1.3'
19
+ spec.add_runtime_dependency 'dry-struct', '~> 1.3.0'
20
+ spec.add_runtime_dependency 'wisper', '2.0.0'
21
+ spec.metadata['rubygems_mfa_required'] = 'true'
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'dry-struct'
2
+ require_relative '../initializer/dry_types'
3
+
4
+ require 'boxenn/errors'
5
+
6
+ module Boxenn
7
+ class Entity < Dry::Struct
8
+ alias assign_attributes new
9
+
10
+ def self.primary_keys
11
+ raise UndefinePrimaryKeys.new(class_name: self.class.name)
12
+ end
13
+
14
+ def primary_keys_hash
15
+ if self.class.primary_keys.all? { |s| attributes.key? s }
16
+ attributes.slice(*self.class.primary_keys)
17
+ else
18
+ raise UnassignPrimaryKeys.new(class_name: self.class.name)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,41 @@
1
+ module Boxenn
2
+ class InvalidPrimaryKey < StandardError
3
+ def initialize(**args)
4
+ super(_message(**args))
5
+ end
6
+
7
+ private
8
+
9
+ def _message(class_name:, provided:, required:)
10
+ if required - provided
11
+ "Primary Key #{required - provided} is missing for class #{class_name.inspect}"
12
+ else
13
+ "#{provided - required} is not a primary key for class #{class_name.inspect}"
14
+ end
15
+ end
16
+ end
17
+
18
+ class UndefinePrimaryKeys < StandardError
19
+ def initialize(**args)
20
+ super(_message(**args))
21
+ end
22
+
23
+ private
24
+
25
+ def _message(class_name:)
26
+ "Primary Key needs to be defined in #{class_name.inspect}"
27
+ end
28
+ end
29
+
30
+ class UnassignPrimaryKeys < StandardError
31
+ def initialize(**args)
32
+ super(_message(**args))
33
+ end
34
+
35
+ private
36
+
37
+ def _message(class_name:)
38
+ "Primary Key needs to be assigned in #{class_name.inspect}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ require 'dry/initializer'
2
+
3
+ module Boxenn
4
+ module Repositories
5
+ class Factory
6
+ extend Dry::Initializer
7
+
8
+ param :entity, default: proc {}
9
+
10
+ def build
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def primary_keys
15
+ entity.primary_keys
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ require 'dry/initializer'
2
+
3
+ module Boxenn
4
+ module Repositories
5
+ class Query
6
+ extend Dry::Initializer
7
+
8
+ param :relation, default: proc {}
9
+
10
+ def collect
11
+ raise NotImplementedError
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Boxenn
2
+ module Repositories
3
+ class RecordMapper
4
+ def build
5
+ raise NotImplementedError
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ require 'dry/initializer'
2
+
3
+ module Boxenn
4
+ module Repositories
5
+ class SourceWrapper
6
+ extend Dry::Initializer
7
+
8
+ param :source, default: proc {}
9
+
10
+ def find_by(primary_keys)
11
+ raise NotImplementedError
12
+ end
13
+
14
+ def save(primary_keys, attributes)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def destroy(primary_keys_hash)
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,63 @@
1
+ require 'dry/initializer'
2
+
3
+ require 'boxenn/errors'
4
+
5
+ module Boxenn
6
+ class Repository
7
+ extend Dry::Initializer
8
+
9
+ option :source_wrapper, default: -> {}
10
+
11
+ option :factory, default: -> {}
12
+
13
+ option :record_mapper, default: -> {}
14
+
15
+ def find_by_identity(**attributes)
16
+ non_primary_keys_provided = (attributes.keys - factory.primary_keys).empty? && (factory.primary_keys - attributes.keys).empty?
17
+ raise InvalidPrimaryKey.new(class_name: self.class.name, provided: attributes.keys, required: factory.primary_keys) unless non_primary_keys_provided
18
+
19
+ record = retrieve_record(attributes)
20
+ record.nil? ? nil : build(record)
21
+ end
22
+
23
+ def find_by_query(query)
24
+ records = query.collect
25
+ records.map { |record| build(record) }
26
+ end
27
+
28
+ def save(entities)
29
+ Array(entities).each do |entity|
30
+ attributes = adapt(entity.to_h)
31
+ identity = adapt(entity.primary_keys_hash)
32
+ save_record(identity, attributes)
33
+ end
34
+ end
35
+
36
+ def destroy(entities)
37
+ Array(entities).each do |entity|
38
+ identity = adapt(entity.primary_keys_hash)
39
+ source_wrapper.destroy(identity)
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def retrieve_record(**attributes)
46
+ hash = attributes.slice(*factory.primary_keys)
47
+ pk_hash = adapt(hash)
48
+ record = source_wrapper.find_by(pk_hash)
49
+ end
50
+
51
+ def save_record(primary_keys, attributes)
52
+ source_wrapper.save(primary_keys, attributes)
53
+ end
54
+
55
+ def build(record)
56
+ factory.build(record)
57
+ end
58
+
59
+ def adapt(hash)
60
+ record_mapper.build(hash)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,30 @@
1
+ require 'ostruct'
2
+ require 'dry/monads/all'
3
+ require 'wisper'
4
+ require 'dry/initializer'
5
+
6
+ module Boxenn
7
+ ##
8
+ # 這個類別是在 Domain(Use case) 層中負責處理業務邏輯的基礎類別
9
+
10
+ class UseCase
11
+ extend Dry::Initializer
12
+ include Wisper::Publisher
13
+ include Dry::Monads
14
+ include Dry::Monads::Do
15
+
16
+ def call(*args)
17
+ Success(yield(steps(*args)))
18
+ rescue Dry::Monads::Do::Halt
19
+ raise
20
+ rescue StandardError => e
21
+ Failure.new([e], trace: e.backtrace.first)
22
+ end
23
+
24
+ protected
25
+
26
+ def steps
27
+ raise NotImplementedError
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Boxenn
2
+ VERSION = '3.0.0'.freeze
3
+ end
data/lib/boxenn.rb ADDED
@@ -0,0 +1,2 @@
1
+ module Boxenn
2
+ end
@@ -0,0 +1,5 @@
1
+ require 'dry-types'
2
+
3
+ module Types
4
+ include Dry.Types()
5
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boxenn
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Michael Fu
8
+ - Oscar
9
+ - Joseph
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2022-01-21 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: dry-initializer
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '3.0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: dry-monads
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '1.3'
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '1.3'
43
+ - !ruby/object:Gem::Dependency
44
+ name: dry-struct
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 1.3.0
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: 1.3.0
57
+ - !ruby/object:Gem::Dependency
58
+ name: wisper
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '='
62
+ - !ruby/object:Gem::Version
63
+ version: 2.0.0
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - '='
69
+ - !ruby/object:Gem::Version
70
+ version: 2.0.0
71
+ description:
72
+ email:
73
+ - oscarada87@gmail.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - LICENSE
79
+ - README.md
80
+ - boxenn.gemspec
81
+ - lib/boxenn.rb
82
+ - lib/boxenn/entity.rb
83
+ - lib/boxenn/errors.rb
84
+ - lib/boxenn/repositories/factory.rb
85
+ - lib/boxenn/repositories/query.rb
86
+ - lib/boxenn/repositories/record_mapper.rb
87
+ - lib/boxenn/repositories/source_wrapper.rb
88
+ - lib/boxenn/repository.rb
89
+ - lib/boxenn/use_case.rb
90
+ - lib/boxenn/version.rb
91
+ - lib/initializer/dry_types.rb
92
+ homepage: https://github.com/sunnyfounder/boxenn
93
+ licenses:
94
+ - MIT
95
+ metadata:
96
+ rubygems_mfa_required: 'true'
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: 2.6.0
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubygems_version: 3.0.8
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: A ddd-oriented infrastructure for ruby/rails projects based on the dry-rb
116
+ ecosystem.
117
+ test_files: []