petra_core 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.rubocop.yml +83 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +74 -0
- data/MIT-LICENSE +20 -0
- data/README.md +726 -0
- data/Rakefile +8 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/examples/continuation_error.rb +125 -0
- data/examples/dining_philosophers.rb +138 -0
- data/examples/showcase.rb +54 -0
- data/lib/petra/components/entries/attribute_change.rb +29 -0
- data/lib/petra/components/entries/attribute_change_veto.rb +37 -0
- data/lib/petra/components/entries/attribute_read.rb +20 -0
- data/lib/petra/components/entries/object_destruction.rb +22 -0
- data/lib/petra/components/entries/object_initialization.rb +19 -0
- data/lib/petra/components/entries/object_persistence.rb +26 -0
- data/lib/petra/components/entries/read_integrity_override.rb +42 -0
- data/lib/petra/components/entry_set.rb +87 -0
- data/lib/petra/components/log_entry.rb +342 -0
- data/lib/petra/components/proxy_cache.rb +209 -0
- data/lib/petra/components/section.rb +543 -0
- data/lib/petra/components/transaction.rb +405 -0
- data/lib/petra/components/transaction_manager.rb +214 -0
- data/lib/petra/configuration/base.rb +132 -0
- data/lib/petra/configuration/class_configurator.rb +309 -0
- data/lib/petra/configuration/configurator.rb +67 -0
- data/lib/petra/core_ext.rb +27 -0
- data/lib/petra/exceptions.rb +181 -0
- data/lib/petra/persistence_adapters/adapter.rb +154 -0
- data/lib/petra/persistence_adapters/file_adapter.rb +239 -0
- data/lib/petra/proxies/abstract_proxy.rb +149 -0
- data/lib/petra/proxies/enumerable_proxy.rb +44 -0
- data/lib/petra/proxies/handlers/attribute_read_handler.rb +45 -0
- data/lib/petra/proxies/handlers/missing_method_handler.rb +47 -0
- data/lib/petra/proxies/method_handlers.rb +213 -0
- data/lib/petra/proxies/module_proxy.rb +12 -0
- data/lib/petra/proxies/object_proxy.rb +310 -0
- data/lib/petra/util/debug.rb +45 -0
- data/lib/petra/util/extended_attribute_accessors.rb +51 -0
- data/lib/petra/util/field_accessors.rb +35 -0
- data/lib/petra/util/registrable.rb +48 -0
- data/lib/petra/util/test_helpers.rb +9 -0
- data/lib/petra/version.rb +5 -0
- data/lib/petra.rb +100 -0
- data/lib/tasks/petra_tasks.rake +5 -0
- data/petra.gemspec +36 -0
- metadata +208 -0
@@ -0,0 +1,310 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'petra/proxies/abstract_proxy'
|
4
|
+
require 'petra/proxies/method_handlers'
|
5
|
+
|
6
|
+
module Petra
|
7
|
+
module Proxies
|
8
|
+
#
|
9
|
+
# To avoid messing with the methods defined by ActiveRecord or similar,
|
10
|
+
# the programmer should use these proxy objects (object.petra.*) which handle
|
11
|
+
# actions on a different level.
|
12
|
+
#
|
13
|
+
# This class is the base proxy class which can be extended to cover
|
14
|
+
# certain behaviours that would be too complex to be put inside the configuration.
|
15
|
+
#
|
16
|
+
class ObjectProxy < AbstractProxy
|
17
|
+
include Comparable
|
18
|
+
|
19
|
+
CLASS_NAMES = %w[Object].freeze
|
20
|
+
|
21
|
+
delegate :to_s, to: :proxied_object
|
22
|
+
|
23
|
+
#
|
24
|
+
# Do not create new proxies for already proxied objects.
|
25
|
+
# Instead, return the current proxy object
|
26
|
+
#
|
27
|
+
def petra(*)
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Creepy!
|
32
|
+
def new(*args)
|
33
|
+
class_method!
|
34
|
+
proxied_object.new(*args).petra
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Access the proxied object publicly from each petra proxy
|
39
|
+
# TODO: This should not leave the proxy!
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# user = User.petra.first
|
43
|
+
# user.unproxied.first_name
|
44
|
+
#
|
45
|
+
def unproxied
|
46
|
+
proxied_object
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Checks whether the given attribute was altered during the current transaction.
|
51
|
+
# Note that an attribute counts as `altered` even if it was reset to its original
|
52
|
+
# value in a later transaction step.
|
53
|
+
#
|
54
|
+
# @deprecated
|
55
|
+
#
|
56
|
+
# TODO: Check for dynamic attribute readers?
|
57
|
+
#
|
58
|
+
def __original_attribute?(attribute_name)
|
59
|
+
!transaction.attribute_value?(self, attribute: attribute_name.to_s)
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Catch all methods which are not defined on this proxy object as they
|
64
|
+
# are most likely meant to go to the proxied object
|
65
|
+
#
|
66
|
+
# Also checks a few special cases like attribute reads/changes.
|
67
|
+
# Please note that a method may be e.g. a persistence method AND an attribute writer
|
68
|
+
# (for normal objects, every attribute write would be persisted to memory), so
|
69
|
+
# we have to execute all matching handlers in a queue.
|
70
|
+
#
|
71
|
+
# rubocop:disable Style/MethodMissing
|
72
|
+
def method_missing(meth, *args, &block)
|
73
|
+
# If no transaction is currently running, we proxy everything
|
74
|
+
# to the original object.
|
75
|
+
unless Petra.transaction_running?
|
76
|
+
Petra.logger.info "No transaction running, proxying #{meth} to original object."
|
77
|
+
return unproxied.public_send(meth, *args, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
# As calling a superclass method in ruby does not cause method calls within this method
|
81
|
+
# to be called within the superclass context, the correct (= the child class') attribute
|
82
|
+
# detectors are run.
|
83
|
+
result = __handlers.execute_missing_queue(meth, *args, block: block) do |queue|
|
84
|
+
queue << :handle_attribute_change if __attribute_writer?(meth)
|
85
|
+
queue << :handle_attribute_read if __attribute_reader?(meth)
|
86
|
+
queue << :handle_dynamic_attribute_read if __dynamic_attribute_reader?(meth)
|
87
|
+
queue << :handle_object_persistence if __persistence_method?(meth)
|
88
|
+
end
|
89
|
+
|
90
|
+
Petra.logger.debug "#{object_class_or_self}##{meth}(#{args.map(&:inspect).join(', ')}) => #{result.inspect}"
|
91
|
+
|
92
|
+
result
|
93
|
+
rescue SystemStackError => e
|
94
|
+
exception = ArgumentError.new("Method '#{meth}' lead to a SystemStackError due to `method_missing`")
|
95
|
+
exception.set_backtrace(e.backtrace.uniq)
|
96
|
+
raise exception
|
97
|
+
end
|
98
|
+
# rubocop:enable Style/MethodMissing
|
99
|
+
|
100
|
+
#
|
101
|
+
# It is necessary to forward #respond_to? queries to
|
102
|
+
# the proxied object as otherwise certain calls, especially from
|
103
|
+
# the Rails framework itself will fail.
|
104
|
+
# Hidden methods are ignored.
|
105
|
+
#
|
106
|
+
def respond_to_missing?(meth, *)
|
107
|
+
proxied_object.respond_to?(meth)
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Generates an ID for the proxied object based on the class configuration.
|
112
|
+
# New objects (= objects which were generated within this transaction) receive
|
113
|
+
# an artificial ID
|
114
|
+
#
|
115
|
+
def __object_id
|
116
|
+
@__object_id ||= if __new?
|
117
|
+
transaction.objects.next_id
|
118
|
+
else
|
119
|
+
object_config(:id_method, proc_expected: true, base: proxied_object)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Generates a unique object key based on the proxied object's class and id
|
125
|
+
#
|
126
|
+
# @return [String] the generated object key
|
127
|
+
#
|
128
|
+
def __object_key
|
129
|
+
[proxied_object.class, __object_id].map(&:to_s).join('/')
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# Generates a unique attribute key based on the proxied object's class, id and a given attribute
|
134
|
+
#
|
135
|
+
# @param [String, Symbol] attribute
|
136
|
+
#
|
137
|
+
# @return [String] the generated attribute key
|
138
|
+
#
|
139
|
+
def __attribute_key(attribute)
|
140
|
+
[proxied_object.class, __object_id, attribute].map(&:to_s).join('/')
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# @return [Boolean] +true+ if the proxied object did not exist before the transaction started
|
145
|
+
#
|
146
|
+
def __new?
|
147
|
+
transaction.objects.new?(self)
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# @return [Boolean] +true+ if the proxied object existed before the transaction started
|
152
|
+
#
|
153
|
+
def __existing?
|
154
|
+
transaction.objects.existing?(self)
|
155
|
+
end
|
156
|
+
|
157
|
+
#
|
158
|
+
# @return [Boolean] +true+ if the proxied object was created (= initialized + persisted) during
|
159
|
+
# the current transaction
|
160
|
+
#
|
161
|
+
def __created?
|
162
|
+
transaction.objects.created?(self)
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# @return [Boolean] +true+ if the proxied object was destroyed during the transaction
|
167
|
+
#
|
168
|
+
def __destroyed?
|
169
|
+
transaction.objects.destroyed?(self)
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Very simple spaceship operator based on the object key
|
174
|
+
# TODO: See if this causes problems when ID-ordering is expected.
|
175
|
+
# For existing objects that shouldn't be the case in most situations as
|
176
|
+
# a collection mostly contains only objects of one kind
|
177
|
+
#
|
178
|
+
def <=>(other)
|
179
|
+
__object_key <=> other.__object_key
|
180
|
+
end
|
181
|
+
|
182
|
+
protected
|
183
|
+
|
184
|
+
#----------------------------------------------------------------
|
185
|
+
# Method Group Detectors
|
186
|
+
#----------------------------------------------------------------
|
187
|
+
|
188
|
+
#
|
189
|
+
# Checks whether the given method name is part of the configured attribute reader
|
190
|
+
# methods within the currently proxied class
|
191
|
+
#
|
192
|
+
def __attribute_reader?(method_name)
|
193
|
+
object_config(:attribute_reader?, method_name.to_s)
|
194
|
+
end
|
195
|
+
|
196
|
+
#
|
197
|
+
# @see #attribute_reader?
|
198
|
+
#
|
199
|
+
def __attribute_writer?(method_name)
|
200
|
+
object_config(:attribute_writer?, method_name.to_s)
|
201
|
+
end
|
202
|
+
|
203
|
+
#
|
204
|
+
# @see #attribute_reader?
|
205
|
+
#
|
206
|
+
# Currently, classes may not use dynamic attribute readers
|
207
|
+
#
|
208
|
+
def __dynamic_attribute_reader?(method_name)
|
209
|
+
!class_proxy? && object_config(:dynamic_attribute_reader?, method_name.to_s)
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# @return [Boolean] +true+ if the given method would persist the
|
214
|
+
# proxied object
|
215
|
+
#
|
216
|
+
def __persistence_method?(method_name)
|
217
|
+
!class_proxy? && object_config(:persistence_method?, method_name.to_s)
|
218
|
+
end
|
219
|
+
|
220
|
+
#
|
221
|
+
# @return [Boolean] +true+ if the given method name is a "destructor" of the
|
222
|
+
# proxied object
|
223
|
+
#
|
224
|
+
def __destruction_method?(method_name)
|
225
|
+
!class_proxy? && object_config(:destruction_method?, method_name.to_s)
|
226
|
+
end
|
227
|
+
|
228
|
+
#
|
229
|
+
# Sets the given attribute to the given value using the default setter
|
230
|
+
# function `name=`. This function is just a convenience method and does not
|
231
|
+
# manage the actual write set. Please take a look at #handle_attribute_change instead.
|
232
|
+
#
|
233
|
+
# @param [String, Symbol] attribute
|
234
|
+
# The attribute name. The proxied object is expected to have a corresponding public setter method
|
235
|
+
#
|
236
|
+
# @param [Object] new_value
|
237
|
+
#
|
238
|
+
def __set_attribute(attribute, new_value)
|
239
|
+
public_send("#{attribute}=", new_value)
|
240
|
+
end
|
241
|
+
|
242
|
+
def __handlers
|
243
|
+
@__handlers ||= Petra::Proxies::MethodHandlers.new(self, binding)
|
244
|
+
end
|
245
|
+
|
246
|
+
def initialize(object, inherited = false, object_id: nil)
|
247
|
+
@obj = object
|
248
|
+
@inherited = inherited
|
249
|
+
@__object_id = object_id
|
250
|
+
end
|
251
|
+
|
252
|
+
#
|
253
|
+
# @return [Object] the proxied object
|
254
|
+
#
|
255
|
+
def proxied_object
|
256
|
+
@obj
|
257
|
+
end
|
258
|
+
|
259
|
+
#
|
260
|
+
# @return [Boolean] +true+ if the proxied object is a class
|
261
|
+
#
|
262
|
+
def class_proxy?
|
263
|
+
proxied_object.is_a?(Class)
|
264
|
+
end
|
265
|
+
|
266
|
+
#
|
267
|
+
# @return [Class] the proxied object if it is a class itself, otherwise
|
268
|
+
# the proxied object's class.
|
269
|
+
#
|
270
|
+
def object_class_or_self
|
271
|
+
class_proxy? ? proxied_object : proxied_object.class
|
272
|
+
end
|
273
|
+
|
274
|
+
#
|
275
|
+
# @return [Boolean] +true+ if the proxied object is a +klass+
|
276
|
+
#
|
277
|
+
def for_class?(klass)
|
278
|
+
proxied_object.is_a?(klass)
|
279
|
+
end
|
280
|
+
|
281
|
+
#
|
282
|
+
# Performs possible type casts on a value which is about to be set
|
283
|
+
# for an attribute. For general ObjectProxy instances, this is simply the identity
|
284
|
+
# function, but it might be overridden in more specialized proxies.
|
285
|
+
#
|
286
|
+
def __type_cast_attribute_value(_attribute, value)
|
287
|
+
value
|
288
|
+
end
|
289
|
+
|
290
|
+
#
|
291
|
+
# Raises an exception if proxied object isn't a class.
|
292
|
+
# Currently, there is no way to specify which methods are class and instance methods
|
293
|
+
# in a specialized proxy, so this at least tells the developer that he did something wrong.
|
294
|
+
#
|
295
|
+
def class_method!
|
296
|
+
return if class_proxy?
|
297
|
+
fail Petra::PetraError, 'This method is meant to be used as a singleton method, not an instance method.'
|
298
|
+
end
|
299
|
+
|
300
|
+
#
|
301
|
+
# @see #class_method!
|
302
|
+
#
|
303
|
+
def instance_method!
|
304
|
+
return if class_proxy?
|
305
|
+
fail Petra::PetraError, 'This method is meant to be used as an instance method only!'
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Petra
|
4
|
+
module Util
|
5
|
+
module Debug
|
6
|
+
STRING_COLORS = {light_gray: 90,
|
7
|
+
yellow: 33,
|
8
|
+
green: 32,
|
9
|
+
red: 31,
|
10
|
+
purple: 35,
|
11
|
+
cyan: 36,
|
12
|
+
blue: 34}.freeze
|
13
|
+
|
14
|
+
FORMATS = {default: 0,
|
15
|
+
bold: 1,
|
16
|
+
underline: 4}.freeze
|
17
|
+
|
18
|
+
%i[debug info warn error].each do |level|
|
19
|
+
define_method level do |message, color = :light_gray, format = :default|
|
20
|
+
log(message, level: level, color: color, format: format)
|
21
|
+
end
|
22
|
+
|
23
|
+
module_function level
|
24
|
+
end
|
25
|
+
|
26
|
+
def log(message, level: :debug, color: :light_gray, format: :default)
|
27
|
+
logger.send(level, 'Petra :: ' + colored_string(message, color, format))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def logger
|
33
|
+
@logger ||= Logger.new(STDOUT).tap do |l|
|
34
|
+
l.level = "Logger::#{Petra.configuration.log_level.upcase}".constantize
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def colored_string(string, color, format)
|
39
|
+
"\e[#{Petra::Util::Debug::FORMATS[format]};#{Petra::Util::Debug::STRING_COLORS[color.to_sym]}m#{string}\e[0m"
|
40
|
+
end
|
41
|
+
|
42
|
+
module_function :log, :logger, :colored_string
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Petra
|
4
|
+
module Util
|
5
|
+
module ExtendedAttributeAccessors
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
extend ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# TODO: Keep track of available accessors on class level
|
14
|
+
# TODO: Keep track of the actual values on instance (or class instance) level
|
15
|
+
|
16
|
+
def extended_attr_accessor(name, **options)
|
17
|
+
singleton = options.fetch(:singleton, false)
|
18
|
+
methods_method, definer_method = :instance_methods, :define_method
|
19
|
+
methods_method, definer_method = :singleton_methods, :define_singleton_method if singleton
|
20
|
+
|
21
|
+
unless send(methods_method).include?(:extended_attribute_accessors)
|
22
|
+
send(definer_method, :__extended_attribute_accessors__) do |group = :general|
|
23
|
+
(@extended_attribute_accessors ||= {})[group.to_sym] ||= {}
|
24
|
+
end
|
25
|
+
|
26
|
+
send(definer_method, :extended_attribute_accessors) do |group = :general, only: nil|
|
27
|
+
result = __extended_attribute_accessors__(group)
|
28
|
+
return result unless only
|
29
|
+
|
30
|
+
result.each_with_object({}) do |(k, v), h|
|
31
|
+
h[k] = v.slice(Array(only).map(:to_sym))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
group = options.fetch(:group, :general)
|
37
|
+
|
38
|
+
send(definer_method, name) do
|
39
|
+
accessor = extended_attribute_accessors(group)[name.to_sym] || {}
|
40
|
+
accessor[:value] || accessor[:default]
|
41
|
+
end
|
42
|
+
|
43
|
+
define_method("#{name}=") do |value|
|
44
|
+
self[name] = value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Petra
|
4
|
+
module Util
|
5
|
+
module FieldAccessors
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def field_accessor(name)
|
12
|
+
define_method(name) do
|
13
|
+
self[name]
|
14
|
+
end
|
15
|
+
|
16
|
+
define_method("#{name}=") do |value|
|
17
|
+
self[name] = value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def fields
|
23
|
+
@fields ||= {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](key)
|
27
|
+
fields[key.to_s]
|
28
|
+
end
|
29
|
+
|
30
|
+
def []=(key, value)
|
31
|
+
fields[key.to_s] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Petra
|
4
|
+
module Util
|
5
|
+
#
|
6
|
+
# Helper module to add register functionality to a class
|
7
|
+
# This means that other classes may register themselves under a certain name
|
8
|
+
#
|
9
|
+
module Registrable
|
10
|
+
def self.included(base)
|
11
|
+
base.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
#
|
16
|
+
# Generates helper methods from the given name.
|
17
|
+
#
|
18
|
+
# @example Type register
|
19
|
+
# acts_as_register(:type)
|
20
|
+
# => registered_types
|
21
|
+
# => register_type(type)
|
22
|
+
# => registered_type(type) #=> value
|
23
|
+
# => registered_type?(type) #=> true/false
|
24
|
+
#
|
25
|
+
def acts_as_register(name)
|
26
|
+
name = name.to_s
|
27
|
+
|
28
|
+
define_singleton_method("registered_#{name.pluralize}") do
|
29
|
+
@registered_components ||= {}
|
30
|
+
@registered_components[name.to_s] ||= {}
|
31
|
+
end
|
32
|
+
|
33
|
+
define_singleton_method("registered_#{name}") do |key|
|
34
|
+
send("registered_#{name.pluralize}")[key.to_s]
|
35
|
+
end
|
36
|
+
|
37
|
+
define_singleton_method("register_#{name}") do |key, value|
|
38
|
+
send("registered_#{name.pluralize}")[key.to_s] = value
|
39
|
+
end
|
40
|
+
|
41
|
+
define_singleton_method("registered_#{name}?") do |key|
|
42
|
+
send("registered_#{name.pluralize}").key?(key.to_s)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/petra.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/all'
|
4
|
+
require 'method_source'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
require 'petra/core_ext'
|
8
|
+
require 'petra/exceptions'
|
9
|
+
require 'petra/configuration/base'
|
10
|
+
require 'petra/configuration/class_configurator'
|
11
|
+
require 'petra/util/debug'
|
12
|
+
require 'petra/persistence_adapters/file_adapter'
|
13
|
+
|
14
|
+
require 'petra/proxies/enumerable_proxy'
|
15
|
+
require 'petra/proxies/object_proxy'
|
16
|
+
|
17
|
+
require 'petra/components/transaction_manager'
|
18
|
+
|
19
|
+
require 'petra/components/entries/attribute_change'
|
20
|
+
require 'petra/components/entries/attribute_change_veto'
|
21
|
+
require 'petra/components/entries/attribute_read'
|
22
|
+
require 'petra/components/entries/object_destruction'
|
23
|
+
require 'petra/components/entries/object_initialization'
|
24
|
+
require 'petra/components/entries/object_persistence'
|
25
|
+
require 'petra/components/entries/read_integrity_override'
|
26
|
+
|
27
|
+
require 'forwardable'
|
28
|
+
|
29
|
+
module Petra
|
30
|
+
extend SingleForwardable
|
31
|
+
|
32
|
+
def self.root
|
33
|
+
Pathname.new(File.dirname(__FILE__))
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# @return [Petra::Configuration::Base] petra's configuration instance
|
38
|
+
#
|
39
|
+
def self.configuration
|
40
|
+
@configuration ||= Petra::Configuration::Base.new
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Executes the given block in the context of petra's configuration instance
|
45
|
+
#
|
46
|
+
def self.configure(&proc)
|
47
|
+
configuration.instance_eval(&proc) if block_given?
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Forward transaction handling to the TransactionManager class
|
52
|
+
#
|
53
|
+
# @see Petra::Components::TransactionManager#with_transaction
|
54
|
+
#
|
55
|
+
def self.transaction(identifier: nil, &block)
|
56
|
+
Petra::Components::TransactionManager.with_transaction(identifier: identifier || SecureRandom.uuid, &block)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# @return [Boolean] +true+ if a transaction is currently running
|
61
|
+
#
|
62
|
+
def self.transaction_running?
|
63
|
+
Petra::Components::TransactionManager.instance?
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Attempts to commit the currently active transaction
|
68
|
+
#
|
69
|
+
def self.commit!
|
70
|
+
transaction_manager.commit_transaction
|
71
|
+
end
|
72
|
+
|
73
|
+
#
|
74
|
+
# @return [Petra::Components::TransactionManager, NilClass]
|
75
|
+
#
|
76
|
+
def self.transaction_manager
|
77
|
+
Petra::Components::TransactionManager.instance
|
78
|
+
end
|
79
|
+
|
80
|
+
def_delegator :transaction_manager, :current_transaction
|
81
|
+
|
82
|
+
#
|
83
|
+
# Logs the given +message+
|
84
|
+
#
|
85
|
+
def self.logger
|
86
|
+
Petra::Util::Debug
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.rails?
|
90
|
+
defined?(Rails)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Extend the Object class to add the `petra` proxy generator
|
95
|
+
Object.class_eval do
|
96
|
+
include Petra::CoreExt::Object
|
97
|
+
end
|
98
|
+
|
99
|
+
# Register Persistence Adapters
|
100
|
+
Petra::PersistenceAdapters::Adapter.register_adapter(:file, Petra::PersistenceAdapters::FileAdapter)
|
data/petra.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'petra/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = 'petra_core'
|
9
|
+
spec.version = Petra::VERSION
|
10
|
+
spec.authors = ['Stefan Exner']
|
11
|
+
spec.email = ['stex@sterex.de']
|
12
|
+
|
13
|
+
spec.summary = 'Proof-Of-Concept for temporarily persisted transactions in Ruby'
|
14
|
+
spec.homepage = 'https://github.com/stex/petra'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.required_ruby_version = '~> 2.5'
|
26
|
+
|
27
|
+
spec.add_dependency 'activesupport', '~> 4.2'
|
28
|
+
spec.add_dependency 'method_source', '~> 0.9.0'
|
29
|
+
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
31
|
+
spec.add_development_dependency 'faker', '~> 1.8'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.11'
|
33
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
35
|
+
spec.add_development_dependency 'rubocop', '~> 0.53.0'
|
36
|
+
end
|