hexx-entities 0.0.1

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.
@@ -0,0 +1,72 @@
1
+ ---
2
+ # settings added by the 'hexx-suit' module
3
+ # output: "tmp/rubocop"
4
+ # format: "html"
5
+
6
+ AllCops:
7
+ Exclude:
8
+ - '**/db/schema.rb'
9
+
10
+ Lint/HandleExceptions:
11
+ Exclude:
12
+ - '**/*_spec.rb'
13
+
14
+ Lint/RescueException:
15
+ Exclude:
16
+ - '**/*_spec.rb'
17
+
18
+ Style/AccessorMethodName:
19
+ Exclude:
20
+ - '**/*_spec.rb'
21
+
22
+ Style/AsciiComments:
23
+ Enabled: false
24
+
25
+ Style/ClassAndModuleChildren:
26
+ Enabled: false
27
+
28
+ Style/Documentation:
29
+ Enabled: false
30
+
31
+ Style/EmptyLinesAroundBlockBody:
32
+ Enabled: false
33
+
34
+ Style/EmptyLinesAroundClassBody:
35
+ Enabled: false
36
+
37
+ Style/EmptyLinesAroundMethodBody:
38
+ Enabled: false
39
+
40
+ Style/EmptyLinesAroundModuleBody:
41
+ Enabled: false
42
+
43
+ Style/EmptyLineBetweenDefs:
44
+ Enabled: false
45
+
46
+ Style/FileName:
47
+ Enabled: false
48
+
49
+ Style/RaiseArgs:
50
+ EnforcedStyle: compact
51
+
52
+ Style/SingleLineMethods:
53
+ Exclude:
54
+ - '**/*_spec.rb'
55
+
56
+ Style/SingleSpaceBeforeFirstArg:
57
+ Enabled: false
58
+
59
+ Style/SpecialGlobalVars:
60
+ Exclude:
61
+ - '**/Gemfile'
62
+ - '**/*.gemspec'
63
+
64
+ Style/StringLiterals:
65
+ EnforcedStyle: double_quotes
66
+
67
+ Style/StringLiteralsInInterpolation:
68
+ EnforcedStyle: double_quotes
69
+
70
+ Style/TrivialAccessors:
71
+ Exclude:
72
+ - '**/*_spec.rb'
@@ -0,0 +1,3 @@
1
+ ---
2
+ warn_cyclo: 4
3
+ error_cyclo: 6
@@ -0,0 +1,8 @@
1
+ ---
2
+ output: tmp/coverage
3
+ filters: # The list of paths to be excluded from coverage checkup
4
+ - "spec/"
5
+ - "config/"
6
+ groups: # The list of groups to be shown in the coverage report
7
+ Libraries: "lib/"
8
+ Application: "app/"
@@ -0,0 +1,37 @@
1
+ ---
2
+ # Settings added by the 'hexx-suit' gem
3
+ output: "tmp/yardstick/output.log"
4
+ path: "lib/**/*.rb"
5
+ rules:
6
+ ApiTag::Presence:
7
+ enabled: true
8
+ exclude: []
9
+ ApiTag::Inclusion:
10
+ enabled: true
11
+ exclude: []
12
+ ApiTag::ProtectedMethod:
13
+ enabled: true
14
+ exclude: []
15
+ ApiTag::PrivateMethod:
16
+ enabled: false
17
+ exclude: []
18
+ ExampleTag:
19
+ enabled: true
20
+ exclude: []
21
+ ReturnTag:
22
+ enabled: true
23
+ exclude: []
24
+ Summary::Presence:
25
+ enabled: true
26
+ exclude: []
27
+ Summary::Length:
28
+ enabled: true
29
+ exclude: []
30
+ Summary::Delimiter:
31
+ enabled: true
32
+ exclude: []
33
+ Summary::SingleLine:
34
+ enabled: true
35
+ exclude: []
36
+ threshold: 100
37
+ verbose: false
@@ -0,0 +1,26 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+ require "hexx/entities/version"
3
+
4
+ Gem::Specification.new do |gem|
5
+
6
+ gem.name = "hexx-entities"
7
+ gem.version = Hexx::Entities::VERSION.dup
8
+ gem.author = "Andrew Kozin"
9
+ gem.email = "andrew.kozin@gmail.com"
10
+ gem.homepage = "https://github.com/nepalez/hexx-entities"
11
+ gem.summary = "Base class for domain entities"
12
+ gem.license = "MIT"
13
+
14
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
15
+ gem.test_files = Dir["spec/**/*.rb"]
16
+ gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.required_ruby_version = "~> 2.1"
20
+
21
+ gem.add_runtime_dependency "attestor", "~> 2.2"
22
+ gem.add_runtime_dependency "eigindir", "~> 0.0"
23
+
24
+ gem.add_development_dependency "hexx-rspec", "~> 0.4"
25
+
26
+ end # Gem::Specification
@@ -0,0 +1,76 @@
1
+ # encoding: utf-8
2
+
3
+ module Hexx::Entities
4
+
5
+ # Class Base provides a common interface for entities
6
+ #
7
+ # @example
8
+ # require "hexx-entities"
9
+ #
10
+ # class Item < Hexx::Entities::Base
11
+ #
12
+ # # Defines attributes with their coercers
13
+ # attribute :foo, coerce: ->(value) { value.to_s }
14
+ #
15
+ # # Defines validation for the entity
16
+ # validate { invalid :blank_foo unless foo }
17
+ #
18
+ # end # class Item
19
+ #
20
+ # @author Andrew Kozin <Andrew.Kozin@gmail.com>
21
+ #
22
+ # @api public
23
+ class Base
24
+ include Attestor::Validations
25
+ include Comparable
26
+ include Eigindir
27
+
28
+ # @!scope class
29
+ # @!method new(attributes)
30
+ # Creates the entity and initializes its attributes
31
+ #
32
+ # @param [Hash] attributes
33
+ #
34
+ # @return [Hexx::Entities::Base]
35
+
36
+ # @private
37
+ def initialize(uuids: nil, **attributes)
38
+ @uuids = UUIDs.build Array(uuids)
39
+ self.attributes = attributes
40
+ end
41
+
42
+ # @!attribute [rw] uuids
43
+ #
44
+ # @return [Array<String>] The list of UUIDs that identify the entity
45
+ attribute_reader :uuids
46
+ validate { uuids.each(&:validate!) }
47
+
48
+ # Checks the equality of the entity to another object
49
+ #
50
+ # The entity is equal to any other entity that has at least one of its
51
+ # own uuids.
52
+ #
53
+ # @param [Object] other
54
+ #
55
+ # @return [Boolean]
56
+ def ==(other)
57
+ return false unless other.is_a? Base
58
+ (other.uuids & uuids).any?
59
+ end
60
+
61
+ # Recursively serializes the entity to the hash
62
+ #
63
+ # Returns the hash of entity attributes, where every attribute
64
+ # that responds to `serialize` is serialized in its turn.
65
+ #
66
+ # @return [Hash]
67
+ def serialize
68
+ values = attributes.values.map do |item|
69
+ item.respond_to?(:serialize) ? item.serialize : item
70
+ end
71
+ attributes.keys.zip(values).to_h
72
+ end
73
+
74
+ end # class Base
75
+
76
+ end # module Hexx::Entities
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ module Hexx::Entities
4
+
5
+ # Class UUID provides validatable value object that describes UUID
6
+ #
7
+ # @example Creates a string
8
+ # uuid = UUID.new "12345678-90ab-cdef-1234-567890abcdef"
9
+ # # => "12345678-90ab-cdef-1234-567890abcdef"
10
+ #
11
+ # @example Validates the format of its own
12
+ # uuid = UUID.new "wrong format"
13
+ # uuid.validate.valid? # => false
14
+ #
15
+ # @example Generates a default value for nil
16
+ # uuid = UUID.new nil
17
+ # # => "97f340af-8157-5c3a-1571-743d208da957"
18
+ #
19
+ # @see https://www.ietf.org/rfc/rfc4122.txt
20
+ # the RFC 4220 standard for Universal Unique Identifiers (UUID)
21
+ #
22
+ # @author Andrew Kozin <Andrew.Kozin@gmail.com>
23
+ #
24
+ # @api public
25
+ class UUID < String
26
+ include Attestor::Validations
27
+
28
+ # @private
29
+ FORMAT = /^\h{8}(-\h{4}){3}-\h{12}$/
30
+
31
+ # @private
32
+ def initialize(value)
33
+ super (value || SecureRandom.uuid).to_s
34
+ end
35
+
36
+ # @private
37
+ validate { invalid :invalid unless self[FORMAT] }
38
+
39
+ end # class UUID
40
+
41
+ end # module Hexx::Entities
@@ -0,0 +1,34 @@
1
+ # encoding: utf-8
2
+
3
+ module Hexx::Entities
4
+
5
+ # Module UUIDs provides a builder utility to create a list of uuids
6
+ #
7
+ # @author Andrew Kozin <Andrew.Kozin@gmail.com>
8
+ #
9
+ module UUIDs
10
+
11
+ # Builds an immutable list of uuids from given list of values
12
+ #
13
+ # @example
14
+ # value = "12345678-90ab-cdef-1234-567890abcdef"
15
+ # list = Hexx::Entities::UUIDs.build value
16
+ #
17
+ # list == [value] # => true
18
+ # list.first.class # => Hexx::Entities::UUID
19
+ # list.frozen? # => true
20
+ #
21
+ # @example Generates default uuid if no uuids given
22
+ # list = Hexx::Entities::UUIDs.build nil
23
+ # list # => ["2bcfaedf-5f62-4929-861a-f8248f2bea16"]
24
+ #
25
+ # @param [Array<String>] values
26
+ #
27
+ # @return [Array<Hexx::Entities::UUID>] immutable (frozen) array of uuids
28
+ def self.build(values)
29
+ (values.empty? ? [nil] : values.uniq).map(&UUID.method(:new)).freeze
30
+ end
31
+
32
+ end # module UUIDs
33
+
34
+ end # module Hexx::Entities
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ module Hexx
4
+
5
+ module Entities
6
+
7
+ # The semantic version of the module.
8
+ # @see http://semver.org/ Semantic versioning 2.0
9
+ VERSION = "0.0.1".freeze
10
+
11
+ end # module Entities
12
+
13
+ end # module Hexx
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ module Hexx
4
+
5
+ # Module Entities provides a base class for domain entities
6
+ #
7
+ # @example (see Hexx::Entities::Base)
8
+ #
9
+ # @author Andrew Kozin <Andrew.Kozin@gmail.com>
10
+ module Entities
11
+
12
+ end # module Entities
13
+
14
+ end # module Hexx
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ require "attestor" # for validations
4
+ require "eigindir" # for attributes
5
+ require "securerandom" # for uuids
6
+
7
+ # Module Hexx provides the shared namespace for 'hexx' collection of gems
8
+ #
9
+ # @author Andrew Kozin <Andrew.Kozin@gmail.com>
10
+ module Hexx
11
+
12
+ require_relative "hexx/entities" # for namespace
13
+ require_relative "hexx/entities/uuid"
14
+ require_relative "hexx/entities/uuids"
15
+ require_relative "hexx/entities/base"
16
+
17
+ end # module Hexx
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ begin
4
+ require "hexx-suit"
5
+ Hexx::Suit.load_metrics_for(self)
6
+ rescue LoadError
7
+ require "hexx-rspec"
8
+ Hexx::RSpec.load_metrics_for(self)
9
+ end
10
+
11
+ # Loads the code under test
12
+ require "hexx-entities"
@@ -0,0 +1,132 @@
1
+ # encoding: utf-8
2
+
3
+ require "attestor/rspec"
4
+
5
+ describe Hexx::Entities::Base do
6
+ include Attestor::RSpec # helpers and matchers for validations
7
+
8
+ let(:klass) { Class.new(described_class) }
9
+
10
+ subject(:entity) { klass.new }
11
+
12
+ describe ".new" do
13
+
14
+ it "creates validatable object" do
15
+ expect(subject).to be_kind_of Attestor::Validations
16
+ end
17
+
18
+ it "creates attributable object" do
19
+ expect(subject).to be_kind_of Eigindir
20
+ end
21
+
22
+ it "creates comparable object" do
23
+ expect(subject).to be_kind_of Comparable
24
+ end
25
+
26
+ it "initializes attributes from hash" do
27
+ klass.attribute :foo
28
+ subject = klass.new foo: :bar
29
+ expect(subject.foo).to eq :bar
30
+ end
31
+
32
+ end # describe .new
33
+
34
+ describe "#uuids" do
35
+
36
+ let(:uuid) { SecureRandom.uuid }
37
+
38
+ it "is an attribute" do
39
+ expect(subject.attributes.keys).to include :uuids
40
+ end
41
+
42
+ it "returns an array" do
43
+ expect(subject.uuids).to be_kind_of Array
44
+ end
45
+
46
+ it "has a default value" do
47
+ expect(subject.uuids.count).to eq 1
48
+ expect(subject.uuids.first).to be_kind_of Hexx::Entities::UUID
49
+ end
50
+
51
+ it "can be initialized" do
52
+ subject = klass.new(uuids: uuid)
53
+ expect(subject.uuids).to eq [uuid]
54
+ expect(subject.uuids.first).to be_kind_of Hexx::Entities::UUID
55
+ end
56
+
57
+ it "is immutable" do
58
+ expect(subject.uuids).to be_frozen
59
+ end
60
+
61
+ end # describe #uuids
62
+
63
+ describe "#==" do
64
+
65
+ let(:subclass) { Class.new(klass) }
66
+ let(:uuid) { SecureRandom.uuid }
67
+ let(:entity) { klass.new uuids: [uuid, SecureRandom.uuid] }
68
+ subject { entity == other }
69
+
70
+ context "to entity with the same uuid" do
71
+
72
+ let(:other) { subclass.new uuids: [SecureRandom.uuid, uuid] }
73
+ it { is_expected.to be true }
74
+
75
+ end # context
76
+
77
+ context "to entity with different uuids" do
78
+
79
+ let(:other) { subclass.new }
80
+ it { is_expected.to be false }
81
+
82
+ end # context
83
+
84
+ context "to non-entity" do
85
+
86
+ let(:other) { double }
87
+ it { is_expected.to be false }
88
+
89
+ end # context
90
+
91
+ end # describe #==
92
+
93
+ describe "#serialize" do
94
+
95
+ before { klass.attribute :foo }
96
+ before { klass.attribute :bar }
97
+
98
+ let(:bar) { double }
99
+ let(:baz) { double serialize: { baz: :baz } }
100
+ let(:uuids) { [SecureRandom.uuid] }
101
+
102
+ subject(:entity) { klass.new(uuids: uuids, foo: bar, bar: baz) }
103
+
104
+ it "returns a nested hash" do
105
+ expect(entity.serialize).to eq(uuids: uuids, foo: bar, bar: { baz: :baz })
106
+ end
107
+
108
+ end # describe #to_hash
109
+
110
+ describe "#validate" do
111
+
112
+ subject { entity.validate }
113
+
114
+ context "when every uuid is valid" do
115
+
116
+ let(:entity) { described_class.new }
117
+
118
+ it { is_expected.to be_valid }
119
+
120
+ end # context
121
+
122
+ context "when any uuid is invalid" do
123
+
124
+ let(:entity) { described_class.new uuids: [SecureRandom.uuid, "wrong"] }
125
+
126
+ it { is_expected.to be_invalid }
127
+
128
+ end # context
129
+
130
+ end # describe #validate
131
+
132
+ end # describe Hexx::Entities::Base
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ require "attestor/rspec"
4
+
5
+ describe Hexx::Entities::UUID do
6
+ include Attestor::RSpec
7
+
8
+ let(:value) { SecureRandom.uuid.to_sym }
9
+ subject(:uuid) { described_class.new value }
10
+
11
+ describe ".new" do
12
+
13
+ context "with something" do
14
+
15
+ it "creates a string" do
16
+ expect(subject).to eq value.to_s
17
+ end
18
+
19
+ end # context
20
+
21
+ context "with nil" do
22
+
23
+ let(:format) { /^\h{8}(-\h{4}){3}-\h{12}$/ }
24
+ subject(:uuid) { described_class.new nil }
25
+
26
+ it "creates uuid by default" do
27
+ expect(subject).to match format
28
+ end
29
+
30
+ end # context
31
+
32
+ context "with nothing" do
33
+
34
+ subject(:uuid) { described_class.new }
35
+
36
+ it "fails" do
37
+ expect { subject }.to raise_error ArgumentError
38
+ end
39
+
40
+ end # context
41
+
42
+ end # describe .new
43
+
44
+ describe "#validate" do
45
+
46
+ subject { uuid.validate }
47
+
48
+ context "with valid uuid" do
49
+
50
+ it { is_expected.to be_valid }
51
+
52
+ end # context
53
+
54
+ context "with invalid value" do
55
+
56
+ let(:value) { "wrong" }
57
+ it { is_expected.to be_invalid }
58
+
59
+ end # context
60
+
61
+ end # describe #validate
62
+
63
+ end # describe Hexx::Entities::UUID
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+
3
+ describe Hexx::Entities::UUIDs do
4
+
5
+ describe ".build" do
6
+
7
+ subject { described_class.build values }
8
+
9
+ shared_examples "building immutable array of uuids" do
10
+
11
+ it "[returns an array]" do
12
+ expect(subject).to be_kind_of Array
13
+ end
14
+
15
+ it "[returns uuids]" do
16
+ subject.each { |item| expect(item).to be_kind_of Hexx::Entities::UUID }
17
+ end
18
+
19
+ it "[freezes the array]" do
20
+ expect(subject).to be_frozen
21
+ end
22
+
23
+ end # context
24
+
25
+ context "with non-empty uniq array" do
26
+
27
+ let(:values) { 2.times.map { SecureRandom.uuid } }
28
+
29
+ it_behaves_like "building immutable array of uuids"
30
+
31
+ it "uses given values" do
32
+ expect(subject).to match_array values
33
+ end
34
+
35
+ end # context
36
+
37
+ context "with non-empty non-uniq array" do
38
+
39
+ let(:values) { [SecureRandom.uuid] * 2 }
40
+
41
+ it_behaves_like "building immutable array of uuids"
42
+
43
+ it "removes duplications" do
44
+ expect(subject).to match_array values.uniq
45
+ end
46
+
47
+ end # context
48
+
49
+ context "with empty array" do
50
+
51
+ let(:values) { [] }
52
+
53
+ it_behaves_like "building immutable array of uuids"
54
+
55
+ it "creates one uuid" do
56
+ expect(subject.count).to eq 1
57
+ end
58
+
59
+ it "creates valid uuid" do
60
+ expect(subject.first.validate).to be_valid
61
+ end
62
+
63
+ end # context
64
+
65
+ end # describe .build
66
+
67
+ end # describe Hexx::Entities::UUIDs