luna_park 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.overcommit.yml +18 -0
- data/.rspec +3 -0
- data/.rubocop.yml +106 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +308 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +182 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/Rakefile +30 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/docs/.nojekyll +0 -0
- data/docs/CNAME +1 -0
- data/docs/README.md +190 -0
- data/docs/_coverpage.md +18 -0
- data/docs/_imgs/adapter.png +0 -0
- data/docs/_imgs/bender.jpeg +0 -0
- data/docs/_imgs/bender_header.jpeg +0 -0
- data/docs/_imgs/collecting.png +0 -0
- data/docs/_imgs/conductor_schema.png +0 -0
- data/docs/_imgs/ddd_header.jpeg +0 -0
- data/docs/_imgs/ddd_map.png +0 -0
- data/docs/_imgs/domain_context.jpeg +0 -0
- data/docs/_imgs/drunk_master.jpg +0 -0
- data/docs/_imgs/full_map.png +0 -0
- data/docs/_imgs/full_map_hight_res.png +0 -0
- data/docs/_imgs/graph_1.png +0 -0
- data/docs/_imgs/graph_2.jpg +0 -0
- data/docs/_imgs/graph_3.png +0 -0
- data/docs/_imgs/graph_4.jpg +0 -0
- data/docs/_imgs/graph_5.jpg +0 -0
- data/docs/_imgs/graph_5.png +0 -0
- data/docs/_imgs/processing.png +0 -0
- data/docs/_imgs/representation.png +0 -0
- data/docs/_imgs/storage.png +0 -0
- data/docs/_imgs/tourtle_context_map.png +0 -0
- data/docs/_imgs/tree.png +0 -0
- data/docs/_imgs/wm.jpeg +0 -0
- data/docs/_media/bender.jpg +0 -0
- data/docs/_media/black_cover.jpg +0 -0
- data/docs/_media/logo.svg +7 -0
- data/docs/_sidebar.md +9 -0
- data/docs/architecture.md +214 -0
- data/docs/google48f1e6f5c35eae5f.html +1 -0
- data/docs/index.html +32 -0
- data/docs/methodology.md +376 -0
- data/docs/patterns/entity.md +193 -0
- data/docs/patterns/sequence.md +332 -0
- data/docs/patterns/service.md +280 -0
- data/docs/patterns/value.md +197 -0
- data/docs/way.md +66 -0
- data/lib/luna_park.rb +72 -0
- data/lib/luna_park/callable.rb +7 -0
- data/lib/luna_park/entities/attributable.rb +18 -0
- data/lib/luna_park/entities/nested.rb +28 -0
- data/lib/luna_park/entities/simple.rb +39 -0
- data/lib/luna_park/errors.rb +16 -0
- data/lib/luna_park/errors/base.rb +244 -0
- data/lib/luna_park/errors/business.rb +9 -0
- data/lib/luna_park/errors/http.rb +90 -0
- data/lib/luna_park/errors/json_parse.rb +11 -0
- data/lib/luna_park/errors/system.rb +9 -0
- data/lib/luna_park/extensions/attributable.rb +26 -0
- data/lib/luna_park/extensions/callable.rb +44 -0
- data/lib/luna_park/extensions/comparable.rb +90 -0
- data/lib/luna_park/extensions/comparable_debug.rb +96 -0
- data/lib/luna_park/extensions/data_mapper.rb +195 -0
- data/lib/luna_park/extensions/dsl/attributes.rb +135 -0
- data/lib/luna_park/extensions/dsl/foreign_key.rb +97 -0
- data/lib/luna_park/extensions/exceptions/substitutive.rb +83 -0
- data/lib/luna_park/extensions/has_errors.rb +125 -0
- data/lib/luna_park/extensions/injector.rb +189 -0
- data/lib/luna_park/extensions/injector/dependencies.rb +74 -0
- data/lib/luna_park/extensions/predicate_attr_accessor.rb +23 -0
- data/lib/luna_park/extensions/repositories/postgres/create.rb +20 -0
- data/lib/luna_park/extensions/repositories/postgres/delete.rb +15 -0
- data/lib/luna_park/extensions/repositories/postgres/read.rb +63 -0
- data/lib/luna_park/extensions/repositories/postgres/update.rb +21 -0
- data/lib/luna_park/extensions/serializable.rb +99 -0
- data/lib/luna_park/extensions/severity_levels.rb +120 -0
- data/lib/luna_park/extensions/typed_attr_accessor.rb +26 -0
- data/lib/luna_park/extensions/validatable.rb +80 -0
- data/lib/luna_park/extensions/validatable/dry.rb +24 -0
- data/lib/luna_park/extensions/wrappable.rb +43 -0
- data/lib/luna_park/forms/simple.rb +63 -0
- data/lib/luna_park/forms/single_item.rb +74 -0
- data/lib/luna_park/handlers/simple.rb +17 -0
- data/lib/luna_park/http/client.rb +328 -0
- data/lib/luna_park/http/request.rb +225 -0
- data/lib/luna_park/http/response.rb +381 -0
- data/lib/luna_park/http/send.rb +103 -0
- data/lib/luna_park/mappers/simple.rb +92 -0
- data/lib/luna_park/notifiers/bugsnag.rb +48 -0
- data/lib/luna_park/notifiers/log.rb +174 -0
- data/lib/luna_park/notifiers/sentry.rb +50 -0
- data/lib/luna_park/repositories/postgres.rb +38 -0
- data/lib/luna_park/repositories/sequel.rb +11 -0
- data/lib/luna_park/repository.rb +9 -0
- data/lib/luna_park/serializers/simple.rb +28 -0
- data/lib/luna_park/tools.rb +19 -0
- data/lib/luna_park/use_cases/scenario.rb +325 -0
- data/lib/luna_park/use_cases/service.rb +13 -0
- data/lib/luna_park/validators/dry.rb +67 -0
- data/lib/luna_park/values/attributable.rb +21 -0
- data/lib/luna_park/values/compound.rb +26 -0
- data/lib/luna_park/values/single.rb +35 -0
- data/lib/luna_park/version.rb +5 -0
- data/luna_park.gemspec +54 -0
- data/node_modules/.yarn-integrity +12 -0
- data/package-lock.json +3 -0
- data/yarn.lock +4 -0
- 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,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
|