isomorfeus-data 2.4.0 → 2.5.0

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.
@@ -1,174 +0,0 @@
1
- module Isomorfeus
2
- module Data
3
- module AttributeSupport
4
- def self.included(base)
5
- base.instance_exec do
6
- def attribute_conditions
7
- @attribute_conditions ||= {}
8
- end
9
-
10
- def indexed_attributes
11
- @indexed_attributes ||= {}
12
- end
13
-
14
- def valid_attribute?(attr_name, val)
15
- Isomorfeus::Props::Validator.new(self.name, attr_name, val, attribute_conditions[attr_name]).validate!
16
- rescue
17
- false
18
- end
19
-
20
- def validate
21
- Isomorfeus::Props::ValidateHashProxy.new
22
- end
23
-
24
- def _validate_attribute(attr_name, val)
25
- Isomorfeus.raise_error(message: "#{self.name}: No such attribute declared: '#{attr_name}'!") unless attribute_conditions.key?(attr_name)
26
- Isomorfeus::Props::Validator.new(self.name, attr_name, val, attribute_conditions[attr_name]).validated_value
27
- end
28
-
29
- def _validate_attributes(attrs)
30
- attribute_conditions.each_key do |attr_name|
31
- if attribute_conditions[attr_name].key?(:required) && attribute_conditions[attr_name][:required] && !attrs.key?(attr_name)
32
- Isomorfeus.raise_error(message: "Required attribute #{attr_name} not given!")
33
- end
34
- attrs[attr_name] = _validate_attribute(attr_name, attrs[attr_name])
35
- end
36
- end
37
- end
38
-
39
- def _validate_attribute(attr_name, val)
40
- self.class._validate_attribute(attr_name, val)
41
- end
42
-
43
- def _validate_attributes(attrs)
44
- self.class._validate_attributes(attrs)
45
- end
46
-
47
- def exclude_attributes(*attrs)
48
- @_excluded_attributes = attrs
49
- end
50
-
51
- def select_attributes(*attrs)
52
- @_selected_attributes = attrs
53
- end
54
-
55
- if RUBY_ENGINE == 'opal'
56
- base.instance_exec do
57
- def attribute(name, options = {})
58
- indexed_attributes[name] = options.delete(:index) if options.key?(:index)
59
- attribute_conditions[name] = options
60
-
61
- define_method(name) do
62
- _get_attribute(name)
63
- end
64
-
65
- define_method("#{name}=") do |val|
66
- val = _validate_attribute(name, val)
67
- changed!
68
- @_changed_attributes[name] = val
69
- end
70
- end
71
- end
72
-
73
- def validate_attributes_function
74
- %x{
75
- if (typeof self.validate_attributes_function === 'undefined') {
76
- self.validate_attributes_function = function(attributes_object) {
77
- try { self.$_validate_attributes(Opal.Hash.$new(attributes_object)) }
78
- catch (e) { return e.message; }
79
- }
80
- }
81
- return self.validate_attributes_function;
82
- }
83
- end
84
-
85
- def validate_attribute_function(attr)
86
- function_name = "validate_attribute_#{attr}_function"
87
- %x{
88
- if (typeof self[function_name] === 'undefined') {
89
- self[function_name] = function(value) {
90
- try { self.$_validate_attribute(attribute, value); }
91
- catch (e) { return e.message; }
92
- }
93
- }
94
- return self[function_name];
95
- }
96
- end
97
-
98
- def _get_attribute(name)
99
- return @_changed_attributes[name] if @_changed_attributes.key?(name)
100
- path = @_store_path + [name]
101
- result = Redux.fetch_by_path(*path)
102
- %x{
103
- if (result === null) { return nil; }
104
- else if (result instanceof Object && !(result instanceof Array)) {
105
- return Opal.Hash.$new(result);
106
- } else { return result; }
107
- }
108
- end
109
-
110
- def attributes
111
- raw_attributes = Redux.fetch_by_path(*@_store_path)
112
- hash = Hash.new(raw_attributes)
113
- hash.merge!(@_changed_attributes) if @_changed_attributes
114
- hash
115
- end
116
-
117
- def _get_selected_attributes
118
- sel_attributes = attributes.dup
119
- if @_selected_attributes && !@_selected_attributes.empty?
120
- sel_attributes.each_key do |attr|
121
- unless @_selected_attributes.include?(attr) || @_selected_attributes.include?(attr)
122
- sel_attributes.delete(attr)
123
- end
124
- end
125
- end
126
- if @_excluded_attributes && !@_excluded_attributes.empty?
127
- @_excluded_attributes.each { |attr| sel_attributes.delete(attr) }
128
- end
129
- sel_attributes
130
- end
131
- else
132
- base.instance_exec do
133
- def attribute(name, options = {})
134
- indexed_attributes[name] = options.delete(:index) if options.key?(:index)
135
- attribute_conditions[name] = options
136
-
137
- define_method(name) do
138
- @_raw_attributes[name]
139
- end
140
-
141
- define_method("#{name}=") do |val|
142
- val = _validate_attribute(name, val)
143
- changed!
144
- @_raw_attributes[name] = val
145
- end
146
- end
147
- end
148
-
149
- def attributes
150
- @_raw_attributes
151
- end
152
-
153
- def _get_selected_attributes
154
- sel_attributes = attributes.transform_keys(&:to_s)
155
- self.class.attribute_conditions.each do |attr, options|
156
- sel_attributes.delete(attr.to_s) if options[:server_only]
157
- end
158
- if @_selected_attributes && !@_selected_attributes.empty?
159
- sel_attributes.each_key do |attr|
160
- unless @_selected_attributes.include?(attr.to_sym) || @_selected_attributes.include?(attr)
161
- sel_attributes.delete(attr)
162
- end
163
- end
164
- end
165
- if @_excluded_attributes && !@_excluded_attributes.empty?
166
- @_excluded_attributes.each { |attr| sel_attributes.delete(attr.to_s) }
167
- end
168
- sel_attributes
169
- end
170
- end
171
- end
172
- end
173
- end
174
- end
@@ -1,80 +0,0 @@
1
- module Isomorfeus
2
- module Data
3
- class DocumentAccelerator
4
- def self.finalize(fer_acc)
5
- proc { fer_acc.close_index }
6
- end
7
-
8
- attr_reader :doc_class, :doc_class_name
9
- attr_accessor :index
10
-
11
- def initialize(doc_class, &block)
12
- @doc_class = doc_class
13
- @doc_class_name = doc_class.name
14
- if block_given?
15
- res = block.call(self)
16
- @index = res unless @index
17
- else
18
- @index_path = File.expand_path(File.join(Isomorfeus.data_documents_path, @doc_class_name.underscore))
19
- open_index
20
- end
21
- ObjectSpace.define_finalizer(self, self.class.finalize(self))
22
- end
23
-
24
- def destroy_index
25
- close_index
26
- FileUtils.rm_rf(@index_path)
27
- end
28
-
29
- def close_index
30
- @index.close
31
- end
32
-
33
- def create_doc(document)
34
- @index.add_document(document)
35
- end
36
-
37
- def destroy_doc(key)
38
- id = get_doc_id(key)
39
- @index.delete(id) if id
40
- true
41
- end
42
-
43
- def load_doc(key)
44
- id = get_doc_id(key)
45
- @index.doc(id)&.load&.to_h if id
46
- end
47
-
48
- def save_doc(key, document)
49
- id = get_doc_id(key)
50
- if id
51
- @index.update(id, document)
52
- true
53
- end
54
- end
55
-
56
- def search_each(query, options, &block)
57
- @index.search_each(query, options, &block)
58
- end
59
-
60
- private
61
-
62
- def get_doc_id(key)
63
- # special characters must be escaped, characters taken from the ferret query parser documentation
64
- escaped_key = key.gsub(/([\\\&\:\(\)\[\]\{\}\!\"\~\^\|\<\>\=\*\?\+\-\s])/, '\\\\\1')
65
- top_docs = @index.search("key:\"#{escaped_key}\"", limit: 1)
66
- id = top_docs.hits[0].doc if top_docs.total_hits == 1
67
- end
68
-
69
- def open_index
70
- FileUtils.mkdir_p(Isomorfeus.data_documents_path) unless Dir.exist?(Isomorfeus.data_documents_path)
71
- field_infos = Isomorfeus::Ferret::Index::FieldInfos.new(store: :yes, index: :yes, term_vector: :with_positions_offsets)
72
- @index = Isomorfeus::Ferret::Index::Index.new(path: @index_path, key: :key, auto_flush: true, lock_retry_time: 5, field_infos: field_infos)
73
- @index.field_infos.add_field(:key, store: :yes, index: :yes, term_vector: :no) unless @index.field_infos[:key]
74
- @doc_class.field_options.each do |field, options|
75
- @index.field_infos.add_field(field, options) unless @index.field_infos[field]
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,79 +0,0 @@
1
- module Isomorfeus
2
- module Data
3
- class ObjectExpander
4
- class << self
5
- def finalize(ins)
6
- proc { ins.environment.close rescue nil }
7
- end
8
- end
9
-
10
- attr_accessor :environment
11
-
12
- def initialize(class_name:, compress: false, &block)
13
- if block_given?
14
- res = block.call(self)
15
- self.environment = res unless self.environment
16
- else
17
- @env_path = File.expand_path(File.join(Isomorfeus.data_object_envs_path, class_name.underscore))
18
- open_environment
19
- end
20
- @db = self.environment.database('objects', create: true)
21
- @index_db = self.environment.database('index', create: true, dupsort: true)
22
- @compress = compress
23
- @use_class_cache = !Isomorfeus.development?
24
- ObjectSpace.define_finalizer(self, self.class.finalize(self))
25
- end
26
-
27
- def create_object(sid_s, obj)
28
- Isomorfeus::Hamster::Marshal.dump(@db, sid_s, obj, class_cache: @use_class_cache, compress: @compress)
29
- end
30
-
31
- def destroy_object(sid_s)
32
- @db.delete(sid_s) rescue nil
33
- true
34
- end
35
-
36
- def load_object(sid_s)
37
- Isomorfeus::Hamster::Marshal.load(@db, sid_s, class_cache: @use_class_cache)
38
- end
39
-
40
- def save_object(sid_s, obj)
41
- Isomorfeus::Hamster::Marshal.dump(@db, sid_s, obj, class_cache: @use_class_cache, compress: @compress)
42
- end
43
-
44
- def index_delete(key, val)
45
- @index_db.delete(key, val) rescue nil
46
- end
47
-
48
- def index_get(key)
49
- @index_db.get(key)
50
- end
51
-
52
- def index_put(key, val)
53
- environment.transaction { @index_db.put(key, val) }
54
- end
55
-
56
- def each(readonly: true, &block)
57
- @db.each(readonly: readonly) do |key, obj|
58
- block.call(Isomorfeus::Hamster::Marshal.unserialize(obj, class_cache: @use_class_cache))
59
- end
60
- end
61
-
62
- def search(val_key, &block)
63
- @index_db.each_value(val_key, &block)
64
- end
65
-
66
- private
67
-
68
- def open_environment
69
- FileUtils.mkdir_p(@env_path) unless Dir.exist?(@env_path)
70
- begin
71
- self.environment = Isomorfeus::Hamster.new(@env_path, mapsize: Isomorfeus.hamster_mapsize)
72
- rescue RangeError
73
- self.environment = Isomorfeus::Hamster.new(@env_path, mapsize: 2_147_483_647)
74
- STDERR.puts "Isomorfeus::Data Warning: Hamster Object Store limited to 2Gb because of platform restrictions."
75
- end
76
- end
77
- end
78
- end
79
- end
@@ -1,10 +0,0 @@
1
- module LucidDocument
2
- class Base
3
- def self.inherited(base)
4
- base.include(LucidDocument::Mixin)
5
- if RUBY_ENGINE != 'opal'
6
- Isomorfeus.add_valid_data_class(base)
7
- end
8
- end
9
- end
10
- end
@@ -1,199 +0,0 @@
1
- module LucidDocument
2
- module Mixin
3
- def self.included(base)
4
- base.include(Isomorfeus::Data::FieldSupport)
5
- base.extend(Isomorfeus::Data::GenericClassApi)
6
- base.include(Isomorfeus::Data::GenericInstanceApi)
7
- base.include(LucidI18n::Mixin)
8
-
9
- base.instance_exec do
10
- def escape_string(s)
11
- s.gsub(/([\\\&\:\(\)\[\]\{\}\!\"\~\^\|\<\>\=\*\?\+\-\s])/, '\\\\\1')
12
- end
13
- end
14
-
15
- def [](name)
16
- send(name)
17
- end
18
-
19
- def []=(name, val)
20
- send("#{name}=", val)
21
- end
22
-
23
- def changed!
24
- @_changed = true
25
- end
26
-
27
- def to_transport
28
- hash = { 'fields' => _get_selected_fields }
29
- hash['revision'] = revision if revision
30
- result = { @class_name => { @key => hash }}
31
- result.deep_merge!(@class_name => { @previous_key => { new_key: @key}}) if @previous_key
32
- result
33
- end
34
-
35
- if RUBY_ENGINE == 'opal'
36
- def initialize(key: nil, revision: nil, fields: nil, _loading: false)
37
- @key = key.nil? ? SecureRandom.uuid : key.to_s
38
- @class_name = self.class.name
39
- @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
40
- _update_paths
41
- @_revision = revision ? revision : Redux.fetch_by_path(:data_state, :revision, @class_name, @key)
42
- @_changed = false
43
- loaded = loaded?
44
- if loaded
45
- raw_fields = Redux.fetch_by_path(*@_store_path)
46
- if `raw_fields === null`
47
- if fields
48
- _validate_fields(fields)
49
- @_changed_fields = fields
50
- else
51
- @_changed_fields = {}
52
- end
53
- elsif raw_fields && fields && ::Hash.new(raw_fields) != fields
54
- _validate_fields(fields)
55
- @_changed_fields = fields
56
- else
57
- @_changed_fields = {}
58
- end
59
- else
60
- fields = {} unless fields
61
- _validate_fields(fields) unless _loading
62
- @_changed_fields = fields
63
- end
64
- end
65
-
66
- def _load_from_store!
67
- @_changed_fields = {}
68
- @_changed = false
69
- end
70
-
71
- def _update_paths
72
- @_store_path = [:data_state, @class_name, @key, :fields]
73
- end
74
-
75
- def each(&block)
76
- fields.each(&block)
77
- end
78
- else # RUBY_ENGINE
79
- Isomorfeus.add_valid_data_class(base) unless base == LucidDocument::Base
80
-
81
- base.instance_exec do
82
- def instance_from_transport(instance_data, _included_items_data)
83
- key = instance_data[self.name].keys.first
84
- revision = instance_data[self.name][key].key?('revision') ? instance_data[self.name][key]['revision'] : nil
85
- fields = instance_data[self.name][key].key?('fields') ? instance_data[self.name][key]['fields'].transform_keys!(&:to_sym) : nil
86
- new(key: key, revision: revision, fields: fields)
87
- end
88
-
89
- def props_from_data(instance_data)
90
- key = instance_data[self.name].keys.first
91
- revision = instance_data[self.name][key].key?('revision') ? instance_data[self.name][key]['revision'] : nil
92
- fields = instance_data[self.name][key].key?('fields') ? instance_data[self.name][key]['fields'].transform_keys!(&:to_sym) : nil
93
- LucidProps.new({ key: key, revision: revision }.merge!(fields))
94
- end
95
-
96
- def setup_index(&block)
97
- @_setup_index_block = block
98
- end
99
-
100
- def ferret_accelerator
101
- return @ferret_accelerator if @ferret_accelerator
102
- @ferret_accelerator = if @_setup_index_block
103
- Isomorfeus::Data::DocumentAccelerator.new(self, &@_setup_index_block)
104
- else
105
- Isomorfeus::Data::DocumentAccelerator.new(self)
106
- end
107
- end
108
-
109
- def each(&block)
110
- self.ferret_accelerator.each do |id|
111
- doc = self.ferret_accelerator.index.doc(id)&.load
112
- if doc
113
- key = doc.delete(:key)
114
- block.call(self.new(key: key, fields: doc))
115
- end
116
- end
117
- end
118
-
119
- def search(query, options = {})
120
- top_docs = []
121
- self.ferret_accelerator.search_each(query, options) do |id|
122
- doc = self.ferret_accelerator.index.doc(id)&.load
123
- if doc
124
- key = doc.delete(:key)
125
- top_docs << self.new(key: key, fields: doc)
126
- end
127
- end
128
- top_docs
129
- end
130
-
131
- execute_create do
132
- doc = self.fields.dup
133
- if self.key.nil?
134
- u = SecureRandom.uuid
135
- doc[:key] = u
136
- self.key = u
137
- else
138
- doc[:key] = self.key
139
- end
140
- self.class.ferret_accelerator.create_doc(doc)
141
- self
142
- end
143
-
144
- execute_destroy do |key:|
145
- self.ferret_accelerator.destroy_doc(key)
146
- end
147
-
148
- execute_load do |key:|
149
- doc = self.ferret_accelerator.load_doc(key)
150
- if doc
151
- doc.delete(:key)
152
- self.new(key: key, fields: doc)
153
- end
154
- end
155
-
156
- execute_save do
157
- doc = self.fields.dup
158
- if self.key.nil?
159
- u = SecureRandom.uuid
160
- doc[:key] = u
161
- self.key = u
162
- self.class.ferret_accelerator.create_doc(doc)
163
- else
164
- doc[:key] = self.key
165
- self.class.ferret_accelerator.save_doc(self.key, doc)
166
- end
167
- self
168
- end
169
- end
170
-
171
- def initialize(key: nil, revision: nil, fields: nil)
172
- @key = key.nil? ? SecureRandom.uuid : key.to_s
173
- @class_name = self.class.name
174
- @class_name = @class_name.split('>::').last if @class_name.start_with?('#<')
175
- @_revision = revision
176
- @_changed = false
177
- fields = {} unless fields
178
- _validate_fields(fields)
179
- @_raw_fields = fields
180
- end
181
-
182
- def _unchange!
183
- @_changed = false
184
- end
185
-
186
- def each(&block)
187
- @_raw_fields.each(&block)
188
- end
189
-
190
- def reload
191
- new_instance = self.class.load(key: @key)
192
- @_raw_fields = new_instance.fields
193
- _unchange!
194
- self
195
- end
196
- end # RUBY_ENGINE
197
- end
198
- end
199
- end