isomorfeus-data 2.5.5 → 22.9.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/isomorfeus/data/config.rb +8 -25
- data/lib/isomorfeus/data/enhancer.rb +291 -0
- data/lib/isomorfeus/data/file_accelerator.rb +95 -0
- data/lib/isomorfeus/data/object_accelerator.rb +82 -90
- data/lib/isomorfeus/data/rack_middleware.rb +73 -0
- data/lib/isomorfeus/data/reducer.rb +47 -17
- data/lib/isomorfeus/data/sid.rb +34 -0
- data/lib/isomorfeus/data/version.rb +1 -1
- data/lib/isomorfeus-data.rb +27 -32
- data/lib/lucid_data_generic.rb +163 -0
- data/lib/lucid_file.rb +137 -0
- data/lib/lucid_object.rb +431 -0
- metadata +51 -47
- data/lib/data_uri/open_uri.rb +0 -22
- data/lib/data_uri/uri.rb +0 -61
- data/lib/data_uri.rb +0 -4
- data/lib/isomorfeus/data/field_support.rb +0 -269
- data/lib/isomorfeus/data/generic_class_api.rb +0 -127
- data/lib/isomorfeus/data/generic_instance_api.rb +0 -171
- data/lib/isomorfeus/data/handler/generic.rb +0 -133
- data/lib/isomorfeus_data/lucid_file/base.rb +0 -10
- data/lib/isomorfeus_data/lucid_file/mixin.rb +0 -281
- data/lib/isomorfeus_data/lucid_object/base.rb +0 -10
- data/lib/isomorfeus_data/lucid_object/mixin.rb +0 -250
- data/lib/isomorfeus_data/lucid_query/base.rb +0 -10
- data/lib/isomorfeus_data/lucid_query/mixin.rb +0 -112
- data/lib/isomorfeus_data/lucid_query_result.rb +0 -113
- data/opal/iso_uri.rb +0 -29
- data/opal/uri/common.rb +0 -18
- data/opal/uri/generic.rb +0 -47
|
@@ -11,7 +11,7 @@ module Isomorfeus
|
|
|
11
11
|
def initialize(ruby_class)
|
|
12
12
|
@object_class = ruby_class
|
|
13
13
|
@object_class_name = ruby_class.name
|
|
14
|
-
@class_cache = Isomorfeus.
|
|
14
|
+
@class_cache = !Isomorfeus.development?
|
|
15
15
|
|
|
16
16
|
@store_path = File.expand_path(File.join(Isomorfeus.data_documents_path, "#{@object_class_name.underscore}"))
|
|
17
17
|
open_store
|
|
@@ -19,25 +19,16 @@ module Isomorfeus
|
|
|
19
19
|
ObjectSpace.define_finalizer(self, self.class.finalize(self))
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
_, iso, type_class_name, key = ref.split('---')
|
|
25
|
-
raise "not a valid object reference '#{ref}'" unless iso == "iso-object-reference"
|
|
26
|
-
raise "invalid data class #{type_class_name}" unless Isomorfeus.valid_data_class_name?(type_class_name)
|
|
27
|
-
type_class = Isomorfeus.cached_data_class(type_class_name)
|
|
28
|
-
type_class.load(key: key, _already_loaded: already_loaded)
|
|
22
|
+
def serialize(o)
|
|
23
|
+
Oj.dump(o, mode: :strict)
|
|
29
24
|
end
|
|
30
25
|
|
|
31
|
-
def
|
|
32
|
-
Oj.
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def unserialize(v)
|
|
36
|
-
Oj.load(v, mode: :object, circular: true, class_cache: @class_cache)
|
|
26
|
+
def unserialize(s)
|
|
27
|
+
Oj.load(s, mode: :strict)
|
|
37
28
|
end
|
|
38
29
|
|
|
39
30
|
def destroy_store
|
|
40
|
-
close_store
|
|
31
|
+
close_store rescue nil
|
|
41
32
|
FileUtils.rm_rf(@store_path)
|
|
42
33
|
end
|
|
43
34
|
|
|
@@ -45,112 +36,113 @@ module Isomorfeus
|
|
|
45
36
|
@store.close
|
|
46
37
|
end
|
|
47
38
|
|
|
39
|
+
def exist?(sid_s:)
|
|
40
|
+
!!get_object_id(sid_s)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def exist_by_field?(field:, value:)
|
|
44
|
+
top_docs = @store.search("#{field}:\"#{serialize(value)}\"")
|
|
45
|
+
top_docs.total_hits > 0
|
|
46
|
+
end
|
|
47
|
+
|
|
48
48
|
def search_each(query, options, &block)
|
|
49
49
|
@store.search_each(query, options, &block)
|
|
50
|
+
rescue => e
|
|
51
|
+
return nil if e.message.include?('frt_sort.c')
|
|
52
|
+
raise e
|
|
50
53
|
end
|
|
51
54
|
|
|
52
55
|
def each(&block)
|
|
53
|
-
|
|
54
|
-
@store.each do |doc|
|
|
55
|
-
hash = doc.to_h do |k, v|
|
|
56
|
-
[k, unserialize_or_load(v, ft[k], {})]
|
|
57
|
-
end
|
|
58
|
-
block.call hash
|
|
59
|
-
end
|
|
56
|
+
@store.each { |doc| block.call(doc[:key]) }
|
|
60
57
|
end
|
|
61
58
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
def create(sid_s:, fields:, level: 10)
|
|
60
|
+
id = get_object_id(sid_s)
|
|
61
|
+
return false if id
|
|
62
|
+
data = fields.to_h do |k, v|
|
|
63
|
+
[k, serialize(v)]
|
|
66
64
|
end
|
|
67
|
-
|
|
65
|
+
data[:_revision] = serialize(1)
|
|
66
|
+
data[:key] = sid_s
|
|
67
|
+
@store.add_document(data)
|
|
68
|
+
true
|
|
69
|
+
rescue => e
|
|
70
|
+
@store.flush
|
|
71
|
+
raise e if level < 1
|
|
72
|
+
create(sid_s: sid_s, fields: fields, level: level - 1)
|
|
68
73
|
end
|
|
69
74
|
|
|
70
|
-
def
|
|
71
|
-
id = get_object_id(
|
|
75
|
+
def destroy(sid_s:)
|
|
76
|
+
id = get_object_id(sid_s)
|
|
72
77
|
@store.delete(id) if id
|
|
73
78
|
end
|
|
74
79
|
|
|
75
|
-
def
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
def load(sid_s: nil, id: nil)
|
|
81
|
+
id = get_object_id(sid_s) if sid_s
|
|
82
|
+
return nil unless id
|
|
83
|
+
data = @store.doc(id)&.to_h
|
|
84
|
+
if data
|
|
85
|
+
data.delete(:key)
|
|
86
|
+
data = data.to_h do |k, v|
|
|
87
|
+
[k, unserialize(v)]
|
|
82
88
|
end
|
|
83
89
|
end
|
|
84
|
-
|
|
90
|
+
data
|
|
85
91
|
end
|
|
86
92
|
|
|
87
|
-
def
|
|
88
|
-
id = get_object_id(
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
end
|
|
94
|
-
@store.update(id, hash.merge!({key: key}))
|
|
95
|
-
true
|
|
93
|
+
def field(sid_s:, field:)
|
|
94
|
+
id = get_object_id(sid_s) if sid_s
|
|
95
|
+
return nil unless id
|
|
96
|
+
doc = @store.doc(id)
|
|
97
|
+
v = doc[field]
|
|
98
|
+
unserialize(v) if v
|
|
96
99
|
end
|
|
97
100
|
|
|
98
|
-
|
|
101
|
+
def load_sid_s(id:)
|
|
102
|
+
doc = @store.doc(id)
|
|
103
|
+
return doc[:key] if doc
|
|
104
|
+
end
|
|
99
105
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
v = v.map { |e| object_from_ref(e, already_loaded) }
|
|
105
|
-
v.compact!
|
|
106
|
-
return v
|
|
107
|
-
else
|
|
108
|
-
return nil if v.empty?
|
|
109
|
-
return object_from_ref(v, already_loaded)
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
v
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def serialize_or_save(v, t, already_saved)
|
|
116
|
-
return serialize(v) if t == :attribute
|
|
117
|
-
if t == :object && v
|
|
118
|
-
if v.is_a?(Array)
|
|
119
|
-
v = v.compact
|
|
120
|
-
v.map! { |e| create_or_save(e, already_saved) }
|
|
121
|
-
v.compact!
|
|
122
|
-
return v
|
|
123
|
-
else
|
|
124
|
-
return create_or_save(v, already_saved)
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
v
|
|
106
|
+
def revision(sid_s:)
|
|
107
|
+
id = get_object_id(sid_s)
|
|
108
|
+
doc = @store.doc(id) if id
|
|
109
|
+
return unserialize(doc[:_revision]) if doc
|
|
128
110
|
end
|
|
129
111
|
|
|
130
|
-
def
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
v
|
|
112
|
+
def save(sid_s:, fields:, revision:, id: nil, level: 10)
|
|
113
|
+
id = get_object_id(sid_s) unless id
|
|
114
|
+
return false unless id
|
|
115
|
+
data = fields.to_h do |k, v|
|
|
116
|
+
[k, serialize(v)]
|
|
135
117
|
end
|
|
136
|
-
|
|
118
|
+
data[:key] = sid_s
|
|
119
|
+
data[:_revision] = serialize(revision)
|
|
120
|
+
@store.update(id, data)
|
|
121
|
+
true
|
|
122
|
+
rescue => e
|
|
123
|
+
@store.flush
|
|
124
|
+
raise e if level < 1
|
|
125
|
+
save(sid_s: sid_s, fields: fields, revision: revision, id: id, level: level - 1)
|
|
137
126
|
end
|
|
138
127
|
|
|
139
|
-
def
|
|
128
|
+
def escape(string)
|
|
140
129
|
# special characters must be escaped, characters taken from the ferret query parser documentation
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
130
|
+
string.gsub(/([\\\&\:\(\)\[\]\{\}\!\"\~\^\|\<\>\=\*\?\+\-\s])/, '\\\\\1')
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def get_object_id(sid_s)
|
|
136
|
+
top_docs = @store.search("key:\"#{sid_s}\"", limit: 1)
|
|
137
|
+
top_docs.hits[0].doc if top_docs.total_hits == 1
|
|
144
138
|
end
|
|
145
139
|
|
|
146
140
|
def open_store
|
|
147
141
|
FileUtils.mkdir_p(Isomorfeus.data_documents_path) unless Dir.exist?(Isomorfeus.data_documents_path)
|
|
148
|
-
field_infos = Isomorfeus::Ferret::Index::FieldInfos.new(store: :yes, index: :
|
|
142
|
+
field_infos = Isomorfeus::Ferret::Index::FieldInfos.new(store: :yes, index: :no, term_vector: :no)
|
|
143
|
+
@object_class.field_options.each { |field, options| field_infos.add_field(field, options) }
|
|
144
|
+
field_infos.add_field(:key, store: :yes, index: :yes, term_vector: :yes)
|
|
149
145
|
@store = Isomorfeus::Ferret::Index::Index.new(path: @store_path, key: :key, auto_flush: true, lock_retry_time: 5, field_infos: field_infos)
|
|
150
|
-
@store.field_infos.add_field(:key, store: :yes, index: :yes, term_vector: :yes) unless @store.field_infos[:key]
|
|
151
|
-
@object_class.field_options.each do |field, options|
|
|
152
|
-
@store.field_infos.add_field(field, options) unless @store.field_infos[field]
|
|
153
|
-
end
|
|
154
146
|
end
|
|
155
147
|
end
|
|
156
148
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Isomorfeus
|
|
4
|
+
module Data
|
|
5
|
+
class RackMiddleware
|
|
6
|
+
NOT_FOUND = [404, {}, ['Not found!']].freeze
|
|
7
|
+
NOT_AUTHORIZED = [401, {}, ['Not authorized!']].freeze
|
|
8
|
+
|
|
9
|
+
FILES_REGEXP = /\A\/files\/([A-Za-z\:]+)\/([A-Za-z0-9\-\/_]+)\z/
|
|
10
|
+
|
|
11
|
+
def initialize(app)
|
|
12
|
+
@app = app
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(env)
|
|
16
|
+
unless env['isomorfeus_store_initialized']
|
|
17
|
+
Isomorfeus.init_store
|
|
18
|
+
Isomorfeus.store.clear!
|
|
19
|
+
env['isomorfeus_store_initialized'] = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
path_info = env['PATH_INFO']
|
|
23
|
+
|
|
24
|
+
if path_info.start_with?(Isomorfeus.api_files_path)
|
|
25
|
+
begin
|
|
26
|
+
m = FILES_REGEXP.match(path_info)
|
|
27
|
+
return NOT_FOUND unless m
|
|
28
|
+
type_class_name = m[1]
|
|
29
|
+
key = m[2]
|
|
30
|
+
|
|
31
|
+
return NOT_FOUND unless Isomorfeus.valid_data_class_name?(type_class_name)
|
|
32
|
+
|
|
33
|
+
type_class = Isomorfeus.cached_data_class(type_class_name)
|
|
34
|
+
return NOT_FOUND unless type_class.ancestors.include?(LucidFile)
|
|
35
|
+
|
|
36
|
+
env['isomorfeus_user_session'] = Isomorfeus::Transport::RackMiddleware.user_from_env(env) unless env.key?('isomorfeus_user_session')
|
|
37
|
+
user, session_id = env['isomorfeus_user_session']
|
|
38
|
+
|
|
39
|
+
authorized = false
|
|
40
|
+
begin
|
|
41
|
+
Thread.current[:isomorfeus_user] = user
|
|
42
|
+
Thread.current[:isomorfeus_session_id] = session_id
|
|
43
|
+
authorized = true if user.authorized?(type_class, :load, key: key)
|
|
44
|
+
ensure
|
|
45
|
+
Thread.current[:isomorfeus_user] = nil
|
|
46
|
+
Thread.current[:isomorfeus_session_id] = nil
|
|
47
|
+
end
|
|
48
|
+
return NOT_AUTHORIZED unless authorized
|
|
49
|
+
data = type_class.file_accelerator.load_data(sid: SID.new(type_class_name, key))
|
|
50
|
+
|
|
51
|
+
return NOT_FOUND unless data
|
|
52
|
+
|
|
53
|
+
mime_type = Marcel::MimeType.for(data)
|
|
54
|
+
headers = { Rack::CONTENT_TYPE => mime_type }
|
|
55
|
+
|
|
56
|
+
return [200, headers, [data]]
|
|
57
|
+
rescue
|
|
58
|
+
return NOT_FOUND
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
@app.call(env)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def check_and_prepare_path(class_name:, key:)
|
|
66
|
+
# '..', '.' and other critical characters are already excluded by the FILES_REGEXP
|
|
67
|
+
elements = key.split('/')
|
|
68
|
+
Isomorfeus.raise_error(message: 'Invalid key (contains more than 2 slashes "/")') if elements.size > 3
|
|
69
|
+
::File.expand_path(::File.join(Isomorfeus.files_path, class_name, *elements))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -1,30 +1,60 @@
|
|
|
1
1
|
module Isomorfeus
|
|
2
2
|
module Data
|
|
3
3
|
module Reducer
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
if RUBY_ENGINE == 'opal'
|
|
5
|
+
CLIENT_REDUCER = proc do |prev_state, action|
|
|
6
6
|
action_type = action[:type]
|
|
7
|
-
if action_type.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
when 'DATA_LOAD'
|
|
16
|
-
prev_state.deep_merge(action[:data])
|
|
7
|
+
if action_type == 'DATA_STATE' && action.key?(:set_state)
|
|
8
|
+
action[:set_state]
|
|
9
|
+
elsif action_type == 'REDUX_MERGE'
|
|
10
|
+
red_state = action.dig(:state, :data_state)
|
|
11
|
+
if red_state
|
|
12
|
+
new_state = prev_state ? prev_state.deep_dup : {}
|
|
13
|
+
new_state.deep_merge!(red_state)
|
|
17
14
|
else
|
|
18
|
-
prev_state
|
|
15
|
+
prev_state || {}
|
|
19
16
|
end
|
|
17
|
+
elsif action_type == 'DATA_MERGE'
|
|
18
|
+
new_state = prev_state ? prev_state.deep_dup : {}
|
|
19
|
+
new_state.deep_merge!(action[:data])
|
|
20
|
+
new_state
|
|
20
21
|
else
|
|
21
|
-
prev_state
|
|
22
|
+
prev_state || {}
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
def self.add_reducer_to_store
|
|
27
|
+
::Redux::Store.preloaded_state_merge!(data_state: {})
|
|
28
|
+
::Redux::Store.add_reducers(data_state: CLIENT_REDUCER)
|
|
29
|
+
end
|
|
30
|
+
else # RUBY_ENGINE
|
|
31
|
+
SERVER_REDUCER = proc do |prev_state, action|
|
|
32
|
+
action_type = action[:type]
|
|
33
|
+
if action_type == 'DATA_STATE' && action.key?(:set_state)
|
|
34
|
+
action[:set_state]
|
|
35
|
+
elsif action_type == 'DATA_MERGE'
|
|
36
|
+
new_state = prev_state ? prev_state.deep_dup : {}
|
|
37
|
+
new_state.deep_merge!(action[:data])
|
|
38
|
+
new_state
|
|
39
|
+
elsif action_type == 'DATA_SET'
|
|
40
|
+
new_state = prev_state ? prev_state.deep_dup : {}
|
|
41
|
+
if !new_state[action[:class_name]]
|
|
42
|
+
new_state[action[:class_name]] = {}
|
|
43
|
+
else
|
|
44
|
+
new_state[action[:class_name]].delete(action[:key])
|
|
45
|
+
end
|
|
46
|
+
new_state[action[:class_name]][action[:key]] = action[:data]
|
|
47
|
+
new_state
|
|
48
|
+
else
|
|
49
|
+
prev_state || {}
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.add_reducer_to_store
|
|
54
|
+
::Redux::Store.preloaded_state_merge!(data_state: {})
|
|
55
|
+
::Redux::Store.add_reducers(data_state: SERVER_REDUCER)
|
|
56
|
+
end
|
|
57
|
+
end # RUBY_ENGINE
|
|
28
58
|
end
|
|
29
59
|
end
|
|
30
60
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
class SID
|
|
2
|
+
SID_S_RE = /\ASID__([a-zA-Z0-9:]+)__([\w\/\-]+)__\z/ # must use this way for compatiblity
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
def from_s(sid_s)
|
|
6
|
+
m = SID_S_RE.match(sid_s)
|
|
7
|
+
self.new(m[1], m[2]) if m
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def is?(string)
|
|
11
|
+
SID_S_RE.match?(string)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
attr_reader :class_name
|
|
16
|
+
attr_reader :key
|
|
17
|
+
|
|
18
|
+
if RUBY_ENGINE == 'opal'
|
|
19
|
+
def initialize(class_name, key)
|
|
20
|
+
@class_name = class_name
|
|
21
|
+
@key = key
|
|
22
|
+
end
|
|
23
|
+
else
|
|
24
|
+
def initialize(class_name, key)
|
|
25
|
+
raise "Invalid class name '#{class_name}!" unless Isomorfeus.valid_data_class_name?(class_name)
|
|
26
|
+
@class_name = class_name
|
|
27
|
+
@key = key
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
"SID__#{@class_name}__#{@key}__"
|
|
33
|
+
end
|
|
34
|
+
end
|
data/lib/isomorfeus-data.rb
CHANGED
|
@@ -1,50 +1,45 @@
|
|
|
1
|
-
require 'base64'
|
|
2
|
-
require 'stringio'
|
|
3
|
-
if RUBY_ENGINE == 'opal'
|
|
4
|
-
require 'iso_uri'
|
|
5
|
-
end
|
|
6
|
-
require 'data_uri'
|
|
7
|
-
require 'securerandom'
|
|
8
|
-
require 'isomorfeus-policy'
|
|
9
1
|
require 'isomorfeus-transport'
|
|
2
|
+
require 'isomorfeus-redux'
|
|
10
3
|
require 'isomorfeus-i18n'
|
|
4
|
+
require 'isomorfeus-preact'
|
|
5
|
+
require 'isomorfeus-policy'
|
|
11
6
|
require 'isomorfeus/data/config'
|
|
12
|
-
require 'isomorfeus/data/
|
|
13
|
-
require 'isomorfeus/data/generic_class_api'
|
|
14
|
-
require 'isomorfeus/data/generic_instance_api'
|
|
7
|
+
require 'isomorfeus/data/sid'
|
|
15
8
|
|
|
16
|
-
if RUBY_ENGINE
|
|
17
|
-
require 'isomorfeus/data/reducer'
|
|
18
|
-
Isomorfeus::Data::Reducer.add_reducer_to_store
|
|
19
|
-
Isomorfeus.zeitwerk.push_dir('isomorfeus_data')
|
|
20
|
-
require_tree 'isomorfeus_data', autoload: true
|
|
21
|
-
Isomorfeus.zeitwerk.push_dir('data')
|
|
22
|
-
else
|
|
9
|
+
if RUBY_ENGINE != 'opal'
|
|
23
10
|
require 'fileutils'
|
|
24
11
|
require 'uri'
|
|
25
12
|
require 'oj'
|
|
26
13
|
require 'active_support'
|
|
27
14
|
require 'active_support/core_ext/hash'
|
|
28
|
-
|
|
15
|
+
require 'marcel'
|
|
29
16
|
require 'isomorfeus-ferret'
|
|
30
17
|
require 'isomorfeus/data/object_accelerator'
|
|
18
|
+
require 'isomorfeus/data/file_accelerator'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'lucid_data_generic'
|
|
22
|
+
require 'lucid_object'
|
|
23
|
+
require 'lucid_file'
|
|
24
|
+
|
|
25
|
+
require 'isomorfeus/data/reducer'
|
|
26
|
+
Isomorfeus::Data::Reducer.add_reducer_to_store
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
require 'isomorfeus/data/enhancer'
|
|
29
|
+
Isomorfeus::Data::Enhancer.add_enhancer_to_store
|
|
30
|
+
|
|
31
|
+
if RUBY_ENGINE == 'opal'
|
|
32
|
+
Isomorfeus.zeitwerk.push_dir('isomorfeus_data')
|
|
33
|
+
require_tree 'isomorfeus_data', autoload: true
|
|
34
|
+
Isomorfeus.zeitwerk.push_dir('data')
|
|
35
|
+
else
|
|
36
|
+
require 'isomorfeus/data/rack_middleware'
|
|
39
37
|
|
|
40
|
-
|
|
38
|
+
Isomorfeus.add_middleware(Isomorfeus::Data::RackMiddleware)
|
|
41
39
|
|
|
42
40
|
require 'iso_opal'
|
|
43
|
-
Opal.append_path(__dir__.untaint) unless IsoOpal.
|
|
44
|
-
uri_path = File.expand_path(File.join(__dir__.untaint, '..', 'opal'))
|
|
45
|
-
Opal.append_path(uri_path) unless IsoOpal.paths.include?(uri_path)
|
|
41
|
+
Opal.append_path(__dir__.untaint) unless IsoOpal.paths_include?(__dir__.untaint)
|
|
46
42
|
|
|
47
43
|
path = File.expand_path(File.join('app', 'data'))
|
|
48
|
-
|
|
49
|
-
Isomorfeus.zeitwerk.push_dir(path)
|
|
44
|
+
Isomorfeus.zeitwerk.push_dir(path) if Dir.exist?(path)
|
|
50
45
|
end
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
class LucidDataGeneric
|
|
2
|
+
class << self
|
|
3
|
+
def create(key: nil, **things)
|
|
4
|
+
new(key: key, **things).create
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def load(key:)
|
|
8
|
+
instance = self.new(key: key, _loading: true)
|
|
9
|
+
instance.loaded? ? instance : load!(key: key, instance: instance)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def destroyed?(key:)
|
|
13
|
+
Isomorfeus.store.dig(:data_state, self.name, key, :revision) == -1
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def current_user
|
|
17
|
+
Isomorfeus.current_user
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
if RUBY_ENGINE == 'opal'
|
|
22
|
+
class << self
|
|
23
|
+
# callbacks
|
|
24
|
+
def before_load(&block); end
|
|
25
|
+
def after_load(&block); end
|
|
26
|
+
def before_create(&block); end
|
|
27
|
+
def after_create(&block); end
|
|
28
|
+
def before_save(&block); end
|
|
29
|
+
def after_save(&block); end
|
|
30
|
+
def before_destroy(&block); end
|
|
31
|
+
def after_destroy(&block); end
|
|
32
|
+
end
|
|
33
|
+
else # RUBY_ENGINE
|
|
34
|
+
class << self
|
|
35
|
+
# callbacks
|
|
36
|
+
|
|
37
|
+
def before_create(&block)
|
|
38
|
+
@before_create_block = block
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def after_create(&block)
|
|
42
|
+
@after_create_block = block
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def before_load(&block)
|
|
46
|
+
@before_load_block = block
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def after_load(&block)
|
|
50
|
+
@after_load_block = block
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def before_save(&block)
|
|
54
|
+
@before_save_block = block
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def after_save(&block)
|
|
58
|
+
@after_save_block = block
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def before_destroy(&block)
|
|
62
|
+
@before_destroy_block = block
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def after_destroy(&block)
|
|
66
|
+
@after_destroy_block = block
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# callback readers
|
|
70
|
+
|
|
71
|
+
attr_reader :before_create_block, :after_create_block
|
|
72
|
+
attr_reader :before_load_block, :after_load_block
|
|
73
|
+
attr_reader :before_save_block, :after_save_block
|
|
74
|
+
attr_reader :before_destroy_block, :after_destroy_block
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def pub_sub_client
|
|
78
|
+
Isomorfeus.pub_sub_client
|
|
79
|
+
end
|
|
80
|
+
end # RUBY_ENGINE
|
|
81
|
+
|
|
82
|
+
def initialize(key: nil)
|
|
83
|
+
@key = key.nil? ? SecureRandom.uuid : key.to_s
|
|
84
|
+
@self_class = self.class
|
|
85
|
+
@class_name = @self_class.name
|
|
86
|
+
@class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
|
|
87
|
+
@changed = false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def loaded?
|
|
91
|
+
revision > 0
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def instance_uuid
|
|
95
|
+
@instance_uuid ||= SecureRandom.uuid
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def state_instance_uuid
|
|
99
|
+
Isomorfeus.store.dig(:data_state, @class_name, @key, :instance_uuid)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def revision
|
|
103
|
+
Isomorfeus.store.dig(:data_state, @class_name, @key, :revision) || 0
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def created?
|
|
107
|
+
(state_instance_uuid == instance_uuid) && (revision == 1)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def saved?
|
|
111
|
+
(state_instance_uuid == instance_uuid) && (revision == (@last_revision + 1))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def reload
|
|
115
|
+
unchange!
|
|
116
|
+
@self_class.load!(key: @key, instance: self)
|
|
117
|
+
self
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def destroy
|
|
121
|
+
@self_class.destroy(key: @key)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def destroyed?
|
|
125
|
+
@self_class.destroyed?(key: @key)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def changed!
|
|
129
|
+
@changed = true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def unchange!
|
|
133
|
+
@changed = false
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def key
|
|
137
|
+
@key
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def key=(k)
|
|
141
|
+
@key = k.to_s
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def changed?
|
|
145
|
+
@changed
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def sid
|
|
149
|
+
SID.new(@class_name, @key)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def to_n
|
|
153
|
+
sid.to_s
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def current_user
|
|
157
|
+
Isomorfeus.current_user
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def pub_sub_client
|
|
161
|
+
Isomorfeus.pub_sub_client
|
|
162
|
+
end
|
|
163
|
+
end
|