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,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luna_park/repository'
4
+ require 'luna_park/extensions/repositories/postgres/create'
5
+ require 'luna_park/extensions/repositories/postgres/read'
6
+ require 'luna_park/extensions/repositories/postgres/update'
7
+ require 'luna_park/extensions/repositories/postgres/delete'
8
+
9
+ module LunaPark
10
+ module Repositories
11
+ class Postgres < LunaPark::Repository
12
+ # Extend your repository class with existed mixins
13
+ # @example
14
+ # class Repo < LunaPark::Repositories::Postgres
15
+ # mixins :create, :delete
16
+ #
17
+ # entity Entities::User
18
+ # mapper Mappers::User
19
+ # end
20
+ #
21
+ # user = Entities::User
22
+ # repo = Repo.new
23
+ # repo.create user
24
+ #
25
+ # @param list [Array,Symbol] list of mixins, possible values: :create, :read, :update, :delete
26
+ # @return nil
27
+ class << self
28
+ def mixins(*list)
29
+ include Extensions::Repositories::Postgres::Create if list.include? :create
30
+ include Extensions::Repositories::Postgres::Read if list.include? :read
31
+ include Extensions::Repositories::Postgres::Update if list.include? :update
32
+ include Extensions::Repositories::Postgres::Delete if list.include? :delete
33
+ nil
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luna_park/repository'
4
+
5
+ module LunaPark
6
+ module Repositories
7
+ # DEPRECATED! Use LunaPark::Repository instead
8
+ # @deprecated
9
+ class Sequel < LunaPark::Repository; end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luna_park/extensions/data_mapper'
4
+
5
+ module LunaPark
6
+ class Repository
7
+ include LunaPark::Extensions::DataMapper
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ # add description
5
+ module Serializers
6
+ class Simple
7
+ def initialize(object)
8
+ @object = object
9
+ end
10
+
11
+ # :nocov:
12
+
13
+ # @abstract
14
+ def to_h
15
+ raise Errors::AbstractMethod
16
+ end
17
+ # :nocov:
18
+
19
+ def to_json(opts = nil)
20
+ JSON.generate(to_h, opts)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :object
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module LunaPark
4
+ module Tools
5
+ # TODO: add descriptions
6
+ class << self
7
+ def if_gem_installed(name, *requirements)
8
+ Gem::Specification.find_by_name name, *requirements
9
+ rescue Gem::MissingSpecError
10
+ false
11
+ else
12
+ yield if block_given?
13
+ true
14
+ end
15
+
16
+ alias gem_installed? if_gem_installed
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,325 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luna_park/errors'
4
+ require 'luna_park/tools'
5
+ require 'luna_park/notifiers/log'
6
+ LunaPark::Tools.if_gem_installed('bugsnag') { require 'luna_park/notifiers/bugsnag' }
7
+ require 'luna_park/extensions/attributable'
8
+ require 'luna_park/extensions/callable'
9
+ require 'luna_park/extensions/has_errors'
10
+
11
+ module LunaPark
12
+ module UseCases
13
+ # The main goal of the use case is a high-level description
14
+ # of the business process. This specific implementation
15
+ # is based on the ideas of Ivar Jacobson from his article
16
+ # Ivar Jacobson: Use Case 2.0.
17
+ #
18
+ # @example Create new user
19
+ # module Errors
20
+ # # To catch the errors, it's should be the error must
21
+ # # be inherited from the class LunaPark::Errors::Business
22
+ # class UserAlreadyExists < LunaPark::Errors::Business
23
+ # message 'Sorry user with this email already created'
24
+ # notify: :info
25
+ # end
26
+ # end
27
+ #
28
+ # class CreateUser < Scenario
29
+ # attr_accessor :email, :password
30
+ #
31
+ # def call!
32
+ # user = Entities::User.new
33
+ # user.email = email
34
+ # user.password = Service::Encode.call(password)
35
+ #
36
+ # DB.transaction do
37
+ # raise Errors::UserAlreadyExists if Repo::Users.exists?(user)
38
+ # Repo::Users.create(user)
39
+ # end
40
+ # end
41
+ # end
42
+ class Scenario
43
+ include Extensions::Attributable
44
+ extend Extensions::Callable
45
+ include Extensions::HasErrors
46
+
47
+ DEFAULT_NOTIFIER = Notifiers::Log.new
48
+
49
+ private_constant :DEFAULT_NOTIFIER
50
+
51
+ INIT = :initialized
52
+ SUCCESS = :success
53
+ FAIL = :fail
54
+
55
+ private_constant :INIT, :SUCCESS, :FAIL
56
+
57
+ # What status is the process of doing the work under the scenario.
58
+ # It can be :initialized, :success, :failure
59
+ #
60
+ # @example when work just started
61
+ # scenario = Scenario.new
62
+ # scenario.state # => :initialized
63
+ #
64
+ # @example on fail
65
+ # scenario.call # Something went wrong
66
+ # scenario.state # => :failure
67
+ #
68
+ # @example on success
69
+ # scenario.call
70
+ # scenario.state # => :success
71
+ attr_reader :state
72
+
73
+ # If a failure occurs during the scenario, then this attribute will contain this error
74
+ # Else it's nil.
75
+ #
76
+ # @example when work just started
77
+ # scenario = Scenario.new
78
+ # scenario.fail # => nil
79
+ #
80
+ # @example on fail
81
+ # class Fail < Errors::Business; end
82
+ # class FailScenario < Scenario
83
+ # def call!
84
+ # raise Fail
85
+ # :result
86
+ # end
87
+ # end
88
+ #
89
+ # scenario = FailScenario.new
90
+ # scenario.call # Something went wrong
91
+ # scenario.fail # => #<Fail: Fail>
92
+ #
93
+ # @example on success
94
+ # scenario.call
95
+ # scenario.fail # => nil
96
+ attr_reader :failure
97
+
98
+ # The result obtained during the execution of the scenario.
99
+ # It's nil on failure scenario.
100
+ #
101
+ # @example when work just started
102
+ # scenario = Scenario.new
103
+ # scenario.data # => nil
104
+ #
105
+ # @example on fail
106
+ # scenario.call # Something went wrong
107
+ # scenario.data # => nil
108
+ #
109
+ # @example on success
110
+ # class SuccessScenario < Scenario
111
+ # def call!
112
+ # :result
113
+ # end
114
+ # end
115
+ #
116
+ # scenario = SuccessScenario.new
117
+ # scenario.call
118
+ # scenario.data # => :result
119
+ attr_reader :data
120
+
121
+ # Current locale
122
+ attr_reader :locale
123
+
124
+ # Initialize new scenario
125
+ #
126
+ # @param notifier - custom notifier for the current instance of scenario
127
+ # @param locale - custom locale for the current instance of scenario
128
+ # @param attrs - the parameters that are needed to implement the scenario, usually the request model
129
+ #
130
+ # @example without parameters
131
+ # class SayHello < Scenario
132
+ # attr_accessor :first_name, :last_name
133
+ #
134
+ # def call!
135
+ # t('hello_my_nme_is', first_name: first_name, last_name: last_name)
136
+ # end
137
+ # end
138
+ #
139
+ # hello = Scenario.new first_name: 'John', last_name: 'Doe'
140
+ # hello.notifier # => Notifiers::Log
141
+ # hello.locale # => nil
142
+ # hello.first_name # => 'John'
143
+ # hello.last_name # => 'Doe'
144
+ # hello.call! # => 'Hello my name is John Doe'
145
+ #
146
+ # @example with custom parameters
147
+ # hello = Scenario.new first_name: 'John', last_name: 'Doe', notifier: Notifier::Bugsnag, locale: :ru
148
+ # hello.notifier # => Notifiers::Bugsnag
149
+ # hello.locale # => :ru
150
+ # hello.first_name # => 'John'
151
+ # hello.last_name # => 'Doe'
152
+ # hello.call! # => 'Добрый день, меня зовут John Doe'
153
+ def initialize(notifier: nil, locale: nil, **attrs)
154
+ set_attributes attrs
155
+ @data = nil
156
+ @failure = nil
157
+ @locale = locale
158
+ @notifier = notifier
159
+ @state = INIT
160
+ end
161
+
162
+ # You must define this action and describe all business logic here.
163
+ # When you run this method - it run as is, and does not change scenario instance.
164
+ #
165
+ # @abstract
166
+ #
167
+ # @example Fail way
168
+ # class Shot < Scenario
169
+ # attr_accessor :lucky_mode
170
+ #
171
+ # def call!
172
+ # raise YouDied, 'Always something went wrong' unless lucky_mode
173
+ # 'All good'
174
+ # end
175
+ # end
176
+ #
177
+ # bad_day = Shot.new lucky_mode: false
178
+ # bad_day.call! # it raise - SomethingWentWrong: Always something went wrong
179
+ # bad_day.state # => :initialized
180
+ #
181
+ # @example Main way
182
+ # good_day = Shot.new lucky_mode: true
183
+ # good_day.call! # => 'All good'
184
+ # good_day.state # => :initialized
185
+ #
186
+ # @example Russian roulette
187
+ # # `.call!` usually use for "scenario in scenario"
188
+ # class RussianRoulette < Scenario
189
+ # def call!
190
+ # [true, true, true, true, true, false].shuffle do |bullet|
191
+ # Shot.call! lucky_mode: bullet
192
+ # end
193
+ # end
194
+ # end
195
+ def call!
196
+ raise Errors::AbstractMethod
197
+ end
198
+
199
+ # You must define this action and describe all business logic here.
200
+ # When you run this method - it run as is, and does not change scenario instance.
201
+ #
202
+ # @abstract
203
+ #
204
+ # @example fail way
205
+ # class YouDied < Errors::Business; end
206
+ #
207
+ # class Shot < Scenario
208
+ # attr_accessor :lucky_mode
209
+ #
210
+ # def call!
211
+ # raise YouDied, 'Always something went wrong' unless lucky_mode
212
+ # 'All good'
213
+ # end
214
+ # end
215
+ #
216
+ # bad_day = Shot.new lucky_mode: false
217
+ # bad_day.call # => #<Shot:0x000055cbee4bc070...>
218
+ # bad_day.success? # => false
219
+ # bad_day.fail? # => true
220
+ # bad_day.data # => nil
221
+ # bad_day.state # => :failure
222
+ # bad_day.fail # => #<YouDied:0x000055cbee4bc071...>
223
+ # bad_day.fail_message # => ''
224
+ #
225
+ # @example main way
226
+ #
227
+ # good_day = Shot.new lucky_mode: true
228
+ # good_day.call! # => 'All good'
229
+ # good_day.state # => :initialized
230
+ #
231
+ # @example Russian roulette
232
+ # class RussianRoulette < Scenario
233
+ # def call!
234
+ # [true, true, true, true, true, false].shuffle do |bullet|
235
+ # Shot.call! lucky_mode: bullet
236
+ # end
237
+ # end
238
+ # end
239
+ def call
240
+ catch { @data = call! }
241
+ self
242
+ end
243
+
244
+ # Return notifier
245
+ def notifier
246
+ @notifier ||= self.class.default_notifier
247
+ end
248
+
249
+ # @return [Boolean] true if the scenario runs unsuccessfully
250
+ def fail?
251
+ state == FAIL
252
+ end
253
+
254
+ alias failure? fail?
255
+
256
+ # @return [Boolean] true if the scenario runs successfully
257
+ def success?
258
+ state == SUCCESS
259
+ end
260
+
261
+ alias succeed? success?
262
+
263
+ # @return [String] fail message
264
+ def failure_message(locale: nil)
265
+ failure&.message(locale: locale || self.locale)
266
+ end
267
+
268
+ class << self
269
+ # @return Default notifier
270
+ def default_notifier
271
+ @default_notifier ||= DEFAULT_NOTIFIER
272
+ end
273
+
274
+ # Set notifier for this class
275
+ #
276
+ # @example set notifier
277
+ # class Foobar < Scenario
278
+ # notify_with Notifier::Bugsnag
279
+ #
280
+ # def call!
281
+ # true
282
+ # end
283
+ # end
284
+ #
285
+ # Foobar.default_notifier # => Notifier::Bugsnag
286
+ # Foobar.new.notifier # => Notifier::Bugsnag
287
+ def notify_with(notifier)
288
+ @default_notifier = notifier
289
+ end
290
+ end
291
+
292
+ private
293
+
294
+ def catch
295
+ yield
296
+ rescue Errors::Base => e
297
+ @state = FAIL
298
+ notify_error e if e.notify?
299
+ handle_error e
300
+ else
301
+ @state = SUCCESS
302
+ end
303
+
304
+ def notify_error(error)
305
+ notifier.post error, lvl: error.notify_lvl
306
+ end
307
+
308
+ def handle_error(error)
309
+ case error
310
+ when Errors::Business then on_catch(error)
311
+ when Errors::System then on_raise(error)
312
+ else raise ArgumentError, "Unknown error action #{error.class}"
313
+ end
314
+ end
315
+
316
+ def on_catch(error)
317
+ @failure = error
318
+ end
319
+
320
+ def on_raise(error)
321
+ raise error.cover_up_backtrace
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'luna_park/extensions/callable'
4
+ require 'luna_park/extensions/has_errors'
5
+
6
+ module LunaPark
7
+ module UseCases
8
+ class Service
9
+ extend Extensions::Callable
10
+ include Extensions::HasErrors
11
+ end
12
+ end
13
+ end