luna_park 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.overcommit.yml +18 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +106 -0
  6. data/.ruby-gemset +1 -0
  7. data/.ruby-version +1 -0
  8. data/.travis.yml +6 -0
  9. data/CHANGELOG.md +308 -0
  10. data/Gemfile +8 -0
  11. data/Gemfile.lock +182 -0
  12. data/LICENSE +21 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +54 -0
  15. data/Rakefile +30 -0
  16. data/bin/console +15 -0
  17. data/bin/setup +8 -0
  18. data/docs/.nojekyll +0 -0
  19. data/docs/CNAME +1 -0
  20. data/docs/README.md +190 -0
  21. data/docs/_coverpage.md +18 -0
  22. data/docs/_imgs/adapter.png +0 -0
  23. data/docs/_imgs/bender.jpeg +0 -0
  24. data/docs/_imgs/bender_header.jpeg +0 -0
  25. data/docs/_imgs/collecting.png +0 -0
  26. data/docs/_imgs/conductor_schema.png +0 -0
  27. data/docs/_imgs/ddd_header.jpeg +0 -0
  28. data/docs/_imgs/ddd_map.png +0 -0
  29. data/docs/_imgs/domain_context.jpeg +0 -0
  30. data/docs/_imgs/drunk_master.jpg +0 -0
  31. data/docs/_imgs/full_map.png +0 -0
  32. data/docs/_imgs/full_map_hight_res.png +0 -0
  33. data/docs/_imgs/graph_1.png +0 -0
  34. data/docs/_imgs/graph_2.jpg +0 -0
  35. data/docs/_imgs/graph_3.png +0 -0
  36. data/docs/_imgs/graph_4.jpg +0 -0
  37. data/docs/_imgs/graph_5.jpg +0 -0
  38. data/docs/_imgs/graph_5.png +0 -0
  39. data/docs/_imgs/processing.png +0 -0
  40. data/docs/_imgs/representation.png +0 -0
  41. data/docs/_imgs/storage.png +0 -0
  42. data/docs/_imgs/tourtle_context_map.png +0 -0
  43. data/docs/_imgs/tree.png +0 -0
  44. data/docs/_imgs/wm.jpeg +0 -0
  45. data/docs/_media/bender.jpg +0 -0
  46. data/docs/_media/black_cover.jpg +0 -0
  47. data/docs/_media/logo.svg +7 -0
  48. data/docs/_sidebar.md +9 -0
  49. data/docs/architecture.md +214 -0
  50. data/docs/google48f1e6f5c35eae5f.html +1 -0
  51. data/docs/index.html +32 -0
  52. data/docs/methodology.md +376 -0
  53. data/docs/patterns/entity.md +193 -0
  54. data/docs/patterns/sequence.md +332 -0
  55. data/docs/patterns/service.md +280 -0
  56. data/docs/patterns/value.md +197 -0
  57. data/docs/way.md +66 -0
  58. data/lib/luna_park.rb +72 -0
  59. data/lib/luna_park/callable.rb +7 -0
  60. data/lib/luna_park/entities/attributable.rb +18 -0
  61. data/lib/luna_park/entities/nested.rb +28 -0
  62. data/lib/luna_park/entities/simple.rb +39 -0
  63. data/lib/luna_park/errors.rb +16 -0
  64. data/lib/luna_park/errors/base.rb +244 -0
  65. data/lib/luna_park/errors/business.rb +9 -0
  66. data/lib/luna_park/errors/http.rb +90 -0
  67. data/lib/luna_park/errors/json_parse.rb +11 -0
  68. data/lib/luna_park/errors/system.rb +9 -0
  69. data/lib/luna_park/extensions/attributable.rb +26 -0
  70. data/lib/luna_park/extensions/callable.rb +44 -0
  71. data/lib/luna_park/extensions/comparable.rb +90 -0
  72. data/lib/luna_park/extensions/comparable_debug.rb +96 -0
  73. data/lib/luna_park/extensions/data_mapper.rb +195 -0
  74. data/lib/luna_park/extensions/dsl/attributes.rb +135 -0
  75. data/lib/luna_park/extensions/dsl/foreign_key.rb +97 -0
  76. data/lib/luna_park/extensions/exceptions/substitutive.rb +83 -0
  77. data/lib/luna_park/extensions/has_errors.rb +125 -0
  78. data/lib/luna_park/extensions/injector.rb +189 -0
  79. data/lib/luna_park/extensions/injector/dependencies.rb +74 -0
  80. data/lib/luna_park/extensions/predicate_attr_accessor.rb +23 -0
  81. data/lib/luna_park/extensions/repositories/postgres/create.rb +20 -0
  82. data/lib/luna_park/extensions/repositories/postgres/delete.rb +15 -0
  83. data/lib/luna_park/extensions/repositories/postgres/read.rb +63 -0
  84. data/lib/luna_park/extensions/repositories/postgres/update.rb +21 -0
  85. data/lib/luna_park/extensions/serializable.rb +99 -0
  86. data/lib/luna_park/extensions/severity_levels.rb +120 -0
  87. data/lib/luna_park/extensions/typed_attr_accessor.rb +26 -0
  88. data/lib/luna_park/extensions/validatable.rb +80 -0
  89. data/lib/luna_park/extensions/validatable/dry.rb +24 -0
  90. data/lib/luna_park/extensions/wrappable.rb +43 -0
  91. data/lib/luna_park/forms/simple.rb +63 -0
  92. data/lib/luna_park/forms/single_item.rb +74 -0
  93. data/lib/luna_park/handlers/simple.rb +17 -0
  94. data/lib/luna_park/http/client.rb +328 -0
  95. data/lib/luna_park/http/request.rb +225 -0
  96. data/lib/luna_park/http/response.rb +381 -0
  97. data/lib/luna_park/http/send.rb +103 -0
  98. data/lib/luna_park/mappers/simple.rb +92 -0
  99. data/lib/luna_park/notifiers/bugsnag.rb +48 -0
  100. data/lib/luna_park/notifiers/log.rb +174 -0
  101. data/lib/luna_park/notifiers/sentry.rb +50 -0
  102. data/lib/luna_park/repositories/postgres.rb +38 -0
  103. data/lib/luna_park/repositories/sequel.rb +11 -0
  104. data/lib/luna_park/repository.rb +9 -0
  105. data/lib/luna_park/serializers/simple.rb +28 -0
  106. data/lib/luna_park/tools.rb +19 -0
  107. data/lib/luna_park/use_cases/scenario.rb +325 -0
  108. data/lib/luna_park/use_cases/service.rb +13 -0
  109. data/lib/luna_park/validators/dry.rb +67 -0
  110. data/lib/luna_park/values/attributable.rb +21 -0
  111. data/lib/luna_park/values/compound.rb +26 -0
  112. data/lib/luna_park/values/single.rb +35 -0
  113. data/lib/luna_park/version.rb +5 -0
  114. data/luna_park.gemspec +54 -0
  115. data/node_modules/.yarn-integrity +12 -0
  116. data/package-lock.json +3 -0
  117. data/yarn.lock +4 -0
  118. metadata +414 -0
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Extensions
5
+ module Injector
6
+ ##
7
+ # Hash for define dependencies in {Injector} extension.
8
+ #
9
+ # Main difference between Hash and Dependencies it is memorization;
10
+ #
11
+ # # hash example
12
+ # i = 0
13
+ #
14
+ # hash = { i: -> { i += 1 } }
15
+ # hash[:i].call # => 1
16
+ # hash[:i].call # => 2
17
+ #
18
+ # # dependencies
19
+ # i = 0
20
+ #
21
+ # dependencies = Dependencies.wrap(i: -> { i += 1 })
22
+ # dependencies.call_with_cache(:i) # => 1
23
+ # dependencies.call_with_cache(:i) # => 1
24
+ #
25
+ class Dependencies < Hash
26
+ class << self
27
+ ##
28
+ # Dependencies.try_convert(obj) -> hash or nil
29
+ #
30
+ # Try to convert obj into a hash, using to_hash method.
31
+ # Returns converted hash or nil if obj cannot be converted
32
+ # for any reason.
33
+ #
34
+ # See {Hash.try_convert}[https://ruby-doc.org/core-2.7.2/Hash.html#method-c-try_convert]
35
+ #
36
+ # Dependencies.try_convert({1=>2}) # => {1=>2}
37
+ # Dependencies.try_convert("1=>2") # => nil
38
+ def try_convert(obj)
39
+ super.nil? ? nil : new.replace(super)
40
+ end
41
+
42
+ alias wrap try_convert
43
+ end
44
+
45
+ ##
46
+ # Run dependency code and cache result.
47
+ #
48
+ # use_case.dependencies[:messenger] # => #<Proc:0x0000564a0d90d438@t.rb:34>
49
+ # use_case.dependencies.call_with_cache(:messenger) # => 'Foobar'
50
+ def call_with_cache(key)
51
+ cache[key] ||= self[key].call
52
+ end
53
+
54
+ def []=(key, _val)
55
+ cache[key] = nil
56
+ super
57
+ end
58
+
59
+ private
60
+
61
+ def cache
62
+ @cache ||= {}
63
+ end
64
+ end
65
+
66
+ # @!parse include Injector::ClassMethods
67
+ # @!parse extend Injector::InstanceMethods
68
+ def self.included(base)
69
+ base.extend ClassMethods
70
+ base.include InstanceMethods
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Extensions
5
+ module PredicateAttrAccessor
6
+ def predicate_attr_accessor(*names)
7
+ attr_writer(*names)
8
+ attr_reader?(*names)
9
+ end
10
+
11
+ alias attr_accessor? predicate_attr_accessor
12
+
13
+ def predicate_attr_reader(*names)
14
+ names.each do |name|
15
+ ivar = :"@#{name}"
16
+ define_method(:"#{name}?") { instance_variable_get(ivar) }
17
+ end
18
+ end
19
+
20
+ alias attr_reader? predicate_attr_reader
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Extensions
5
+ module Repositories
6
+ module Postgres
7
+ module Create
8
+ def create(input)
9
+ entity = wrap(input)
10
+ row = to_row(entity)
11
+ new_row = dataset.returning.insert(row).first
12
+ new_attrs = from_row(new_row)
13
+ entity.set_attributes(new_attrs)
14
+ entity
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Extensions
5
+ module Repositories
6
+ module Postgres
7
+ module Delete
8
+ def delete(uid)
9
+ dataset.returning.where(primary_key => uid).delete
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Extensions
5
+ module Repositories
6
+ module Postgres
7
+ module Read
8
+ def find!(pk_value, for_update: false)
9
+ ds = dataset.where(primary_key => pk_value)
10
+ read_one!(ds, for_update: for_update, not_found_meta: pk_value)
11
+ end
12
+
13
+ def find(pk_value, for_update: false)
14
+ ds = dataset.where(primary_key => pk_value)
15
+ read_one(ds, for_update: for_update)
16
+ end
17
+
18
+ def lock!(pk_value)
19
+ lock(pk_value) || raise(Errors::NotFound, "#{short_class_name} (#{pk_value})")
20
+ end
21
+
22
+ def lock(pk_value)
23
+ dataset.for_update.select(primary_key).where(primary_key => pk_value).first ? true : false
24
+ end
25
+
26
+ def count
27
+ dataset.count
28
+ end
29
+
30
+ def all
31
+ read_all(dataset.order { created_at.desc })
32
+ end
33
+
34
+ def last
35
+ to_entity from_row dataset.order(:created_at).last
36
+ end
37
+
38
+ private
39
+
40
+ def read_one!(dataset, for_update: false, not_found_meta:)
41
+ read_one(dataset, for_update: for_update).tap do |entity|
42
+ raise Errors::NotFound, "#{short_class_name} (#{not_found_meta})" if entity.nil?
43
+ end
44
+ end
45
+
46
+ def read_one(dataset, for_update: false)
47
+ dataset = dataset.for_update if for_update
48
+ row = dataset.first
49
+ to_entity from_row(row)
50
+ end
51
+
52
+ def read_all(dataset)
53
+ to_entities from_rows(dataset)
54
+ end
55
+
56
+ def short_class_name
57
+ @short_class_name ||= self.class.name[/::(\w+)\z/, 1]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Extensions
5
+ module Repositories
6
+ module Postgres
7
+ module Update
8
+ def save(input)
9
+ entity = wrap(input)
10
+ entity.updated_at = Time.now.utc
11
+ row = to_row(entity)
12
+ new_row = dataset.returning.where(primary_key => row[primary_key]).update(row).first
13
+ new_attrs = from_row(new_row)
14
+ entity.set_attributes(new_attrs)
15
+ entity
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luna_park/errors'
4
+
5
+ module LunaPark
6
+ module Extensions
7
+ # @example
8
+ # class Money
9
+ # include LunaPark::Extensions::Comparable
10
+ #
11
+ # attr_accessor :amount, :currency, :meta
12
+ #
13
+ # serializable_attributes :amount, :currency
14
+ # end
15
+ #
16
+ # money = Money.new
17
+ # money.to_h # => {}
18
+ # money.amount = 1
19
+ # money.to_h # => { amount: 1 }
20
+ # money.currency = 'USD'
21
+ # money.meta = 'meta'
22
+ # money.to_h # => { amount: 1, currency: 'USD' }
23
+ module Serializable
24
+ def self.included(base)
25
+ base.extend ClassMethods
26
+ base.include InstanceMethods
27
+ end
28
+
29
+ module ClassMethods
30
+ ##
31
+ # Describe methods list that will be used for serialization via `#to_h` and `#serialize` methods
32
+ def serializable_attributes(*names)
33
+ raise 'No attributes given' if names.compact.empty?
34
+
35
+ @serializable_attributes_list ||= []
36
+ @serializable_attributes_list |= names
37
+ end
38
+
39
+ ##
40
+ # List of methods that will be used for serialization via `#to_h` and `#serialize` methods
41
+ def serializable_attributes_list
42
+ return @serializable_attributes_list if @serializable_attributes_list
43
+
44
+ raise Errors::NotConfigured,
45
+ "You must set at least one serializable attribute using #{self}.serializable_attributes(*names)"
46
+ end
47
+
48
+ private
49
+
50
+ def inherited(child)
51
+ super
52
+ child.instance_variable_set(:@serializable_attributes_list, @serializable_attributes_list&.dup)
53
+ end
54
+ end
55
+
56
+ module InstanceMethods
57
+ ##
58
+ # Serialize object using methods, described with `::comparable_attributes` method
59
+ def serialize
60
+ self.class
61
+ .serializable_attributes_list
62
+ .each_with_object({}) do |field, output|
63
+ next unless instance_variable_defined?(:"@#{field}")
64
+
65
+ output[field] = serialize_value__(send(field))
66
+ end
67
+ end
68
+
69
+ ##
70
+ # For powerfull polymorphism with Hashes
71
+ alias to_h serialize
72
+
73
+ def inspect
74
+ attrs = self.class.serializable_attributes_list.map do |attr|
75
+ value = instance_variable_get(:"@#{attr}")
76
+ "#{attr}=#{value.inspect}" if value
77
+ end
78
+ "#<#{self.class.name} #{attrs.compact.join(' ')}>"
79
+ end
80
+
81
+ private
82
+
83
+ SERIALIZABLE = ->(o) { o.respond_to?(:serialize) }.freeze
84
+ HASHABLE = ->(o) { o.respond_to?(:to_h) }.freeze
85
+
86
+ def serialize_value__(value) # rubocop:disable Metrics/CyclomaticComplexity
87
+ case value
88
+ when Array then value.map { |v| serialize_value__(v) } # TODO: work with Array (wrap values)
89
+ when Hash then value.transform_values { |v| serialize_value__(v) }
90
+ when nil then nil
91
+ when SERIALIZABLE then value.serialize
92
+ when HASHABLE then value.to_h
93
+ else value
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Extensions
5
+ module SeverityLevels
6
+ # This is class define interface for loggers and notifiers behavior.
7
+ # In main idea it based on rfc5424 https://tools.ietf.org/html/rfc5424, but
8
+ # in fact default ruby logger does not define all severities, and we use only
9
+ # most important:
10
+ # - unknown: an unknown message that should always be logged
11
+ # - fatal: An unhandleable error that results in a program crash
12
+ # - error: system work incorrectly, and maintainer should know about that immediately
13
+ # - warning: warning conditions, and maintainer should know about that, but not immediately
14
+ # - info: informational messages, maintainer should know about that, if they want to analyse logs
15
+ # - debug: debug messages, for developers don't use it on production
16
+ #
17
+ # @example
18
+ # class ChattyLogger < LunaPark::Notifiers::Abstract
19
+ # def message(obj, _details:, lvl:)
20
+ # puts "{lvl.upcase}: #{message}"
21
+ # end
22
+ # end
23
+ #
24
+ # logger = ChattyLogger.new min_lvl: :warning
25
+ # logger.unknown 'Do not do that.' # => 'UNKNOWN: Do not do that.'
26
+ # logger.fatal 'Do not do that.' # => 'FATAL: Do not do that.'
27
+ # logger.error 'Do not do that.' # => 'ERROR: Do not do that.'
28
+ # logger.warning 'Do not do that.' # => 'WARNING: Do not do that.'
29
+ # logger.info 'Do not do that.' # => nil
30
+ # logger.debug 'Do not do that.' # => nil
31
+ LEVELS = %i[debug info warning error fatal unknown].freeze
32
+
33
+ # Defined minimum severity level
34
+ def min_lvl
35
+ @min_lvl ||= :debug
36
+ end
37
+
38
+ def min_lvl=(value)
39
+ raise ArgumentError, 'Undefined severity level' unless LEVELS.include? value
40
+
41
+ @min_lvl = value
42
+ end
43
+
44
+ # rubocop:disable Style/GuardClause
45
+
46
+ # Post message with UNKNOWN severity level
47
+ #
48
+ # @param msg [String,Exception]
49
+ # @param details [Hash]
50
+ def unknown(msg = '', **details)
51
+ message = block_given? ? yield : msg
52
+ post message, lvl: :unknown, **details
53
+ end
54
+
55
+ # Post message with FATAL severity level
56
+ #
57
+ # @param msg [String,Exception]
58
+ # @param details [Hash]
59
+ def fatal(msg = '', **details)
60
+ if %i[debug info warning error fatal].include? min_lvl
61
+ message = block_given? ? yield : msg
62
+ post message, lvl: :fatal, **details
63
+ end
64
+ end
65
+
66
+ # Post message with ERROR severity level
67
+ #
68
+ # @param msg [String,Exception]
69
+ # @param details [Hash]
70
+ def error(msg = '', **details)
71
+ if %i[debug info warning error].include? min_lvl
72
+ message = block_given? ? yield : msg
73
+ post message, lvl: :error, **details
74
+ end
75
+ end
76
+
77
+ # Post stdout message with WARNING severity level
78
+ #
79
+ # @param msg [String,Exception]
80
+ # @param details [Hash]
81
+ def warning(msg = '', **details)
82
+ if %i[debug info warning].include? min_lvl
83
+ message = block_given? ? yield : msg
84
+ post message, lvl: :warning, **details
85
+ end
86
+ end
87
+
88
+ # Post message with INFO severity level
89
+ #
90
+ # @example
91
+ #
92
+ # @param msg [String,Exception]
93
+ # @param details [Hash]
94
+ def info(msg = '', **details)
95
+ if %i[debug info].include? min_lvl
96
+ message = block_given? ? yield : msg
97
+ post message, lvl: :info, **details
98
+ end
99
+ end
100
+
101
+ # Post message with DEBUG severity level
102
+ #
103
+ # @param msg [String,Exception]
104
+ # @param details [Hash]
105
+ def debug(msg = '', **details)
106
+ if min_lvl == :debug
107
+ message = block_given? ? yield : msg
108
+ post message, lvl: :debug, **details
109
+ end
110
+ end
111
+
112
+ # rubocop:enable Style/GuardClause
113
+
114
+ # @abstract
115
+ def post(_msg = '', _lvl:, **_details)
116
+ raise Errors::AbstractMethod
117
+ end
118
+ end
119
+ end
120
+ end