hexx-entities 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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