isomorfeus-data 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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