hyper-mesh 1.0.0.lap23 → 1.0.0.lap24
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/DOCS.md +1 -0
- data/Rakefile +2 -2
- data/hyper-mesh.gemspec +1 -2
- data/lib/active_record_base.rb +249 -106
- data/lib/hyper-mesh.rb +3 -3
- data/lib/hypermesh/version.rb +1 -1
- data/lib/reactive_record/active_record/base.rb +3 -1
- data/lib/reactive_record/active_record/class_methods.rb +88 -13
- data/lib/reactive_record/active_record/instance_methods.rb +15 -1
- data/lib/reactive_record/active_record/reactive_record/base.rb +2 -2
- data/lib/reactive_record/active_record/reactive_record/collection.rb +17 -5
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +19 -12
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +3 -2
- data/lib/reactive_record/server_data_cache.rb +20 -13
- metadata +23 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f71958166fcd1ea5b7b054c460a431f45ebae9389098d3046e805a38225235d6
|
|
4
|
+
data.tar.gz: 9bcf2cb4090bc56920db979d5e016fdcfa450497c375b8487efa8e94e73da837
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59e3c865aa085a2a16ee573d8dcd147d0dc96d91e04fa4799e29e3dc80cce5aba5801e837686a612c2ac72b15f42a21767c8250695552a882c24fd1bdd34fb41
|
|
7
|
+
data.tar.gz: aaa9fb686b480a3ad322e835fa7f8a95a13e37f4a508a374c94aa75585f12f03eb5053f4d33c819269a09f8864f27d67ed639f7146992e96f2c89555307e6d9a
|
data/DOCS.md
CHANGED
|
@@ -617,6 +617,7 @@ Pusher.app_id = "MY_TEST_ID" # you use the real or fake values
|
|
|
617
617
|
Pusher.key = "MY_TEST_KEY"
|
|
618
618
|
Pusher.secret = "MY_TEST_SECRET"
|
|
619
619
|
# The next line actually starts the pusher-fake server (see the Pusher-Fake readme for details.)
|
|
620
|
+
# it is important this require be AFTER the above settings, as it will use these
|
|
620
621
|
require 'pusher-fake/support/base' # if using pusher with rspec change this to pusher-fake/support/rspec
|
|
621
622
|
# now copy over the credentials, and merge with PusherFake's config details
|
|
622
623
|
Hyperloop.configuration do |config|
|
data/Rakefile
CHANGED
|
@@ -4,7 +4,7 @@ require "rspec/core/rake_task"
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
task :spec do
|
|
7
|
-
(1..
|
|
7
|
+
(1..6).each { |batch| Rake::Task["spec:batch#{batch}"].invoke }
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
namespace :spec do
|
|
@@ -12,7 +12,7 @@ namespace :spec do
|
|
|
12
12
|
sh %{bundle update}
|
|
13
13
|
sh %{cd spec/test_app; bundle update; bundle exec rails db:setup} # may need ;bundle exec rails db:setup as well
|
|
14
14
|
end
|
|
15
|
-
(1..
|
|
15
|
+
(1..6).each do |batch|
|
|
16
16
|
RSpec::Core::RakeTask.new(:"batch#{batch}") do |t|
|
|
17
17
|
t.pattern = "spec/batch#{batch}/**/*_spec.rb"
|
|
18
18
|
end
|
data/hyper-mesh.gemspec
CHANGED
|
@@ -25,8 +25,6 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.test_files = `git ls-files -- {spec}/*`.split("\n")
|
|
26
26
|
spec.require_paths = ['lib']
|
|
27
27
|
|
|
28
|
-
spec.post_install_message = "\033[0;31;1mhyper-mesh #{Hypermesh::VERSION} is in development and has known security issues! Not recommended for production use!\033[0;30;21m"
|
|
29
|
-
|
|
30
28
|
spec.add_dependency 'activerecord', '>= 4.0.0'
|
|
31
29
|
spec.add_dependency 'hyper-component', Hypermesh::VERSION
|
|
32
30
|
spec.add_dependency 'hyper-operation', Hypermesh::VERSION
|
|
@@ -65,4 +63,5 @@ Gem::Specification.new do |spec|
|
|
|
65
63
|
spec.add_development_dependency 'timecop', '~> 0.8.1'
|
|
66
64
|
spec.add_development_dependency 'unparser'
|
|
67
65
|
spec.add_development_dependency 'pry'
|
|
66
|
+
spec.add_development_dependency 'pry-rescue'
|
|
68
67
|
end
|
data/lib/active_record_base.rb
CHANGED
|
@@ -1,131 +1,293 @@
|
|
|
1
|
+
# Monkey patches to ActiveRecord for scoping, security, and to synchronize models
|
|
1
2
|
module ActiveRecord
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# hyperloop adds new features to scopes to allow for computing scopes on client side
|
|
4
|
+
# and for hinting at what joins are involved in a scope. _synchromesh_scope_args_check
|
|
5
|
+
# processes these arguments, and the will always leave the true server side scoping
|
|
6
|
+
# proc in the `:server` opts. This method is common to client and server.
|
|
5
7
|
class Base
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
def self._synchromesh_scope_args_check(args)
|
|
9
|
+
opts = if args.count == 2 && args[1].is_a?(Hash)
|
|
10
|
+
args[1].merge(server: args[0])
|
|
11
|
+
elsif args[0].is_a? Hash
|
|
12
|
+
args[0]
|
|
13
|
+
else
|
|
14
|
+
{ server: args[0] }
|
|
15
|
+
end
|
|
16
|
+
return opts if opts && opts[:server].respond_to?(:call)
|
|
17
|
+
raise 'must provide either a proc as the first arg or by the '\
|
|
18
|
+
'`:server` option to scope and default_scope methods'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
if RUBY_ENGINE != 'opal'
|
|
22
|
+
# __synchromesh_permission_granted indicates if permission has been given to return a scope
|
|
23
|
+
# The acting_user attribute is set to the current acting_user so regulation methods can check it
|
|
24
|
+
# The __secure_collection_check method is called at the end of a scope chain and will fail if
|
|
25
|
+
# no scope in the chain has positively granted access.
|
|
26
|
+
|
|
27
|
+
# allows us to easily handle scopes and finder_methods which return arrays of items
|
|
28
|
+
# (instead of ActiveRecord::Relations - see below)
|
|
29
|
+
class ReactiveRecordPsuedoRelationArray < Array
|
|
30
|
+
attr_accessor :__synchromesh_permission_granted
|
|
31
|
+
attr_accessor :acting_user
|
|
32
|
+
def __secure_collection_check(_acting_user)
|
|
33
|
+
self
|
|
20
34
|
end
|
|
35
|
+
end
|
|
21
36
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
# add the __synchromesh_permission_granted, acting_user and __secure_collection_check
|
|
38
|
+
# methods to Relation
|
|
39
|
+
class Relation
|
|
40
|
+
attr_accessor :__synchromesh_permission_granted
|
|
41
|
+
attr_accessor :acting_user
|
|
42
|
+
def __secure_collection_check(acting_user)
|
|
43
|
+
return self if __synchromesh_permission_granted
|
|
44
|
+
return self if __secure_remote_access_to_unscoped(acting_user).__synchromesh_permission_granted
|
|
45
|
+
denied!
|
|
26
46
|
end
|
|
47
|
+
end
|
|
48
|
+
# Monkey patches and extensions to base
|
|
49
|
+
class Base
|
|
50
|
+
class << self
|
|
51
|
+
# every method call that is legal from the client has a wrapper method prefixed with
|
|
52
|
+
# __secure_remote_access_to_
|
|
53
|
+
|
|
54
|
+
# The wrapper method may simply return the normal result or may act to secure the data.
|
|
55
|
+
# The simpliest case is for the method to call `denied!` which will raise a Hyperloop
|
|
56
|
+
# access protection fault.
|
|
57
|
+
|
|
58
|
+
def denied!
|
|
59
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation
|
|
60
|
+
end
|
|
27
61
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
if RUBY_ENGINE != 'opal'
|
|
33
|
-
|
|
34
|
-
alias pre_synchromesh_default_scope default_scope
|
|
62
|
+
# Here we set up the base `all` and `unscoped` methods. See below for more on how
|
|
63
|
+
# access protection works on relationships.
|
|
35
64
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
pre_synchromesh_scope(name, opts[:server], &block)
|
|
65
|
+
def __secure_remote_access_to_all(_acting_user)
|
|
66
|
+
all
|
|
39
67
|
end
|
|
40
68
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
pre_synchromesh_default_scope(opts[:server], &block)
|
|
69
|
+
def __secure_remote_access_to_unscoped(_acting_user, *args)
|
|
70
|
+
unscoped(*args)
|
|
44
71
|
end
|
|
45
72
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
# finder_method and server_method provide secure RPCs against AR relations and records.
|
|
74
|
+
# The block is called in context with the object, and acting_user is set to the
|
|
75
|
+
# current acting user. The block may interogate acting_user to insure security as needed.
|
|
76
|
+
|
|
77
|
+
# For finder_method we have to preapply `all` so that we always have a relationship
|
|
49
78
|
|
|
50
79
|
def finder_method(name, &block)
|
|
51
|
-
singleton_class.send(:define_method, "
|
|
52
|
-
|
|
80
|
+
singleton_class.send(:define_method, :"__secure_remote_access_to__#{name}") do |acting_user, *args|
|
|
81
|
+
this = respond_to?(:acting_user) ? self : all
|
|
82
|
+
begin
|
|
83
|
+
old = this.acting_user
|
|
84
|
+
this.acting_user = acting_user
|
|
85
|
+
# returns a PsuedoRelationArray which will respond to the
|
|
86
|
+
# __secure_collection_check method
|
|
87
|
+
ReactiveRecordPsuedoRelationArray.new([this.instance_exec(*args, &block)])
|
|
88
|
+
ensure
|
|
89
|
+
this.acting_user = old
|
|
90
|
+
end
|
|
53
91
|
end
|
|
54
92
|
singleton_class.send(:define_method, name) do |*args|
|
|
55
|
-
|
|
93
|
+
all.instance_exec(*args, &block)
|
|
56
94
|
end
|
|
57
95
|
end
|
|
58
96
|
|
|
59
|
-
|
|
97
|
+
def server_method(name, _opts = {}, &block)
|
|
98
|
+
# callable from the server internally
|
|
99
|
+
define_method(name, &block)
|
|
100
|
+
# callable remotely from the client
|
|
101
|
+
define_method("__secure_remote_access_to_#{name}") do |acting_user, *args|
|
|
102
|
+
begin
|
|
103
|
+
old = self.acting_user
|
|
104
|
+
self.acting_user = acting_user
|
|
105
|
+
send(name, *args)
|
|
106
|
+
ensure
|
|
107
|
+
self.acting_user = old
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
60
111
|
|
|
61
|
-
|
|
112
|
+
# relationships (and scopes) are regulated using a tri-state system. Each
|
|
113
|
+
# remote access method will return the relationship as normal but will also set
|
|
114
|
+
# the value of __secure_remote_access_granted using the application defined regulation.
|
|
115
|
+
# Each regulation can explicitly allow the scope to be chained by returning a truthy
|
|
116
|
+
# value from the regulation. Or each regulation can explicitly deny the scope to
|
|
117
|
+
# be chained by called `denied!`. Otherwise each regulation can return a falsy
|
|
118
|
+
# value meaning the scope can be changed, but unless some other scope (before or
|
|
119
|
+
# after) in the chain explicitly allows the scope, the entire chain will fail.
|
|
120
|
+
|
|
121
|
+
# In otherwords within a chain of relationships and scopes, at least one Regulation
|
|
122
|
+
# must be return a truthy value otherwise the whole chain fails. Likewise if any
|
|
123
|
+
# regulation called `deined!` the whole chain fails.
|
|
124
|
+
|
|
125
|
+
# If no regulation is defined, the regulation is inherited from the superclass, and if
|
|
126
|
+
# no regulation is defined anywhere in the class heirarchy then the regulation will
|
|
127
|
+
# return a falsy value.
|
|
128
|
+
|
|
129
|
+
# regulations on scopes are inheritable. That is if a superclass defines a regulation
|
|
130
|
+
# for a scope, subclasses will inherit the regulation (but can override)
|
|
131
|
+
|
|
132
|
+
# helper method to sort out the options on the regulate_scope, regulate_relationship macros.
|
|
133
|
+
|
|
134
|
+
# We allow three forms:
|
|
135
|
+
# regulate_xxx name &block : the block is the regulation
|
|
136
|
+
# regulate_xxx name: const : const can be denied!, deny, denied, or any other truthy or
|
|
137
|
+
# falsy value
|
|
138
|
+
# regulate_xxx name: proc : the proc is the regulation
|
|
139
|
+
|
|
140
|
+
def __synchromesh_parse_regulator_params(name, block)
|
|
141
|
+
if name.is_a? Hash
|
|
142
|
+
name, block = name.first
|
|
143
|
+
if %i[denied! deny denied].include? block
|
|
144
|
+
block = ->(*_args) { denied! }
|
|
145
|
+
elsif !block.is_a? Proc
|
|
146
|
+
value = block
|
|
147
|
+
block = ->(*_args) { value }
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
[name, block || ->(*_args) { true }]
|
|
151
|
+
end
|
|
62
152
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
153
|
+
# helper method for providing a regulation in line with a scope or relationship
|
|
154
|
+
# this is done using the `regulate` key on the opts.
|
|
155
|
+
# if no regulate key is provided and there is no regulation already defined for
|
|
156
|
+
# this name, then we create one that returns nil (don't care)
|
|
157
|
+
# once we have things figured out, we yield to the provided proc which is either
|
|
158
|
+
# regulate_scope or regulate_relationship
|
|
159
|
+
|
|
160
|
+
def __synchromesh_regulate_from_macro(opts, name, already_defined)
|
|
161
|
+
if opts.key?(:regulate)
|
|
162
|
+
yield name => opts[:regulate]
|
|
163
|
+
elsif !already_defined
|
|
164
|
+
yield name => ->(*_args) {}
|
|
68
165
|
end
|
|
69
|
-
pre_synchromesh_method_missing(name, *args, &block)
|
|
70
166
|
end
|
|
71
167
|
|
|
72
|
-
|
|
73
|
-
|
|
168
|
+
# helper method to set the value of __synchromesh_permission_granted on the relationship
|
|
169
|
+
# Get the current value of __synchromesh_permission_granted, set acting_user on the
|
|
170
|
+
# object, and or in the result of running the block in context of the obj
|
|
171
|
+
|
|
172
|
+
def __set_synchromesh_permission_granted(r, obj, acting_user, args = [], &block)
|
|
173
|
+
r.__synchromesh_permission_granted = try(:__synchromesh_permission_granted)
|
|
174
|
+
old = acting_user
|
|
175
|
+
obj.acting_user = acting_user
|
|
176
|
+
r.__synchromesh_permission_granted ||= obj.instance_exec(*args, &block)
|
|
177
|
+
r
|
|
178
|
+
ensure
|
|
179
|
+
obj.acting_user = old
|
|
74
180
|
end
|
|
75
181
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
182
|
+
# regulate scope has to deal with the special case that the scope returns an
|
|
183
|
+
# an array instead of a relationship. In this case we wrap the array and go on
|
|
184
|
+
|
|
185
|
+
def regulate_scope(name, &block)
|
|
186
|
+
name, block = __synchromesh_parse_regulator_params(name, block)
|
|
187
|
+
singleton_class.send(:define_method, :"__secure_remote_access_to_#{name}") do |acting_user, *args|
|
|
188
|
+
r = send(name, *args)
|
|
189
|
+
r = ReactiveRecordPsuedoRelationArray.new(r) if r.is_a? Array
|
|
190
|
+
__set_synchromesh_permission_granted(r, r, acting_user, args, &block)
|
|
85
191
|
end
|
|
86
192
|
end
|
|
87
193
|
|
|
194
|
+
# regulate_default_scope
|
|
195
|
+
|
|
196
|
+
def regulate_default_scope(&block)
|
|
197
|
+
regulate_scope(:all, &block)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# monkey patch scope and default_scope macros to process hyperloop special opts,
|
|
201
|
+
# and add regulations if present
|
|
202
|
+
|
|
203
|
+
alias pre_synchromesh_scope scope
|
|
204
|
+
|
|
205
|
+
def scope(name, *args, &block)
|
|
206
|
+
__synchromesh_regulate_from_macro(
|
|
207
|
+
(opts = _synchromesh_scope_args_check(args)),
|
|
208
|
+
name,
|
|
209
|
+
respond_to?(:"__secure_remote_access_to_#{name}"),
|
|
210
|
+
&method(:regulate_scope)
|
|
211
|
+
)
|
|
212
|
+
pre_synchromesh_scope(name, opts[:server], &block)
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
alias pre_synchromesh_default_scope default_scope
|
|
216
|
+
|
|
88
217
|
def default_scope(*args, &block)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
218
|
+
__synchromesh_regulate_from_macro(
|
|
219
|
+
(opts = _synchromesh_scope_args_check([*block, *args])),
|
|
220
|
+
:all,
|
|
221
|
+
respond_to?(:__secure_remote_access_to_all),
|
|
222
|
+
&method(:regulate_scope)
|
|
223
|
+
)
|
|
224
|
+
pre_synchromesh_default_scope(opts[:server], &block)
|
|
92
225
|
end
|
|
93
226
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
227
|
+
# add regulate_relationship method and monkey patch monkey patch has_many macro
|
|
228
|
+
# to add regulations if present
|
|
229
|
+
|
|
230
|
+
def regulate_relationship(name, &block)
|
|
231
|
+
name, block = __synchromesh_parse_regulator_params(name, block)
|
|
232
|
+
define_method(:"__secure_remote_access_to_#{name}") do |acting_user, *args|
|
|
233
|
+
self.class.__set_synchromesh_permission_granted(
|
|
234
|
+
send(name, *args), self, acting_user, &block
|
|
235
|
+
)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
alias pre_syncromesh_has_many has_many
|
|
240
|
+
|
|
241
|
+
def has_many(name, *args, &block)
|
|
242
|
+
__synchromesh_regulate_from_macro(
|
|
243
|
+
opts = args.extract_options!,
|
|
244
|
+
name,
|
|
245
|
+
method_defined?(:"__secure_remote_access_to_#{name}"),
|
|
246
|
+
&method(:regulate_relationship)
|
|
247
|
+
)
|
|
248
|
+
pre_syncromesh_has_many name, *args, opts.except(:regulate), &block
|
|
104
249
|
end
|
|
105
250
|
|
|
106
|
-
|
|
107
|
-
|
|
251
|
+
# add secure access for find, find_by, and belongs_to and has_one relations.
|
|
252
|
+
# No explicit security checks are needed here, as the data returned by these objects
|
|
253
|
+
# will be further processedand checked before returning. I.e. it is not possible to
|
|
254
|
+
# simply return `find(1)` but if you try returning `find(1).name` the permission system
|
|
255
|
+
# will check to see if the name attribute can be legally sent to the current acting user.
|
|
256
|
+
|
|
257
|
+
def __secure_remote_access_to_find(_acting_user, *args)
|
|
258
|
+
find(*args)
|
|
108
259
|
end
|
|
109
260
|
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
ReactiveRecord::Collection
|
|
113
|
-
.new(self, nil, nil, self, 'unscoped')
|
|
114
|
-
.extend(ReactiveRecord::UnscopedCollection)
|
|
261
|
+
def __secure_remote_access_to_find_by(_acting_user, *args)
|
|
262
|
+
find_by(*args)
|
|
115
263
|
end
|
|
116
264
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
265
|
+
%i[belongs_to has_one].each do |macro|
|
|
266
|
+
alias_method :"pre_syncromesh_#{macro}", macro
|
|
267
|
+
define_method(macro) do |name, scope = nil, opts = {}, &block|
|
|
268
|
+
define_method(:"__secure_remote_access_to_#{name}") do |_acting_user, *args|
|
|
269
|
+
send(name, *args)
|
|
122
270
|
end
|
|
271
|
+
send(:"pre_syncromesh_#{macro}", name, scope, opts, &block)
|
|
123
272
|
end
|
|
124
273
|
end
|
|
125
274
|
end
|
|
126
|
-
end
|
|
127
275
|
|
|
128
|
-
|
|
276
|
+
def denied!
|
|
277
|
+
Hyperloop::InternalPolicy.raise_operation_access_violation
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# call do_not_synchronize to block synchronization of a model
|
|
281
|
+
|
|
282
|
+
def self.do_not_synchronize
|
|
283
|
+
@do_not_synchronize = true
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
# used by the broadcast mechanism to determine if this model is to be synchronized
|
|
287
|
+
|
|
288
|
+
def self.do_not_synchronize?
|
|
289
|
+
@do_not_synchronize
|
|
290
|
+
end
|
|
129
291
|
|
|
130
292
|
def do_not_synchronize?
|
|
131
293
|
self.class.do_not_synchronize?
|
|
@@ -136,7 +298,7 @@ module ActiveRecord
|
|
|
136
298
|
after_commit :synchromesh_after_destroy, on: [:destroy]
|
|
137
299
|
|
|
138
300
|
def synchromesh_after_create
|
|
139
|
-
return if do_not_synchronize?
|
|
301
|
+
return if do_not_synchronize?
|
|
140
302
|
ReactiveRecord::Broadcast.after_commit :create, self
|
|
141
303
|
end
|
|
142
304
|
|
|
@@ -149,27 +311,8 @@ module ActiveRecord
|
|
|
149
311
|
return if do_not_synchronize?
|
|
150
312
|
ReactiveRecord::Broadcast.after_commit :destroy, self
|
|
151
313
|
end
|
|
152
|
-
else
|
|
153
|
-
|
|
154
|
-
scope :limit, ->() {}
|
|
155
|
-
scope :offset, ->() {}
|
|
156
|
-
|
|
157
|
-
def update_attribute(attr, value, &block)
|
|
158
|
-
send("#{attr}=", value)
|
|
159
|
-
save(validate: false, &block)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def update(attrs = {}, &block)
|
|
163
|
-
attrs.each { |attr, value| send("#{attr}=", value) }
|
|
164
|
-
save(&block)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def <=>(other)
|
|
168
|
-
id.to_i <=> other.id.to_i
|
|
169
|
-
end
|
|
170
314
|
end
|
|
171
315
|
end
|
|
172
316
|
|
|
173
317
|
InternalMetadata.do_not_synchronize if defined? InternalMetadata
|
|
174
|
-
|
|
175
318
|
end
|
data/lib/hyper-mesh.rb
CHANGED
|
@@ -22,12 +22,12 @@ if RUBY_ENGINE == 'opal'
|
|
|
22
22
|
require "reactive_record/active_record/reactive_record/collection"
|
|
23
23
|
require "reactive_record/active_record/reactive_record/scoped_collection"
|
|
24
24
|
require "reactive_record/active_record/reactive_record/unscoped_collection"
|
|
25
|
+
require "reactive_record/interval"
|
|
26
|
+
require_relative 'active_record_base'
|
|
27
|
+
require_relative 'reactive_record/scope_description'
|
|
25
28
|
require "reactive_record/active_record/class_methods"
|
|
26
29
|
require "reactive_record/active_record/instance_methods"
|
|
27
30
|
require "reactive_record/active_record/base"
|
|
28
|
-
require "reactive_record/interval"
|
|
29
|
-
require_relative 'reactive_record/scope_description'
|
|
30
|
-
require_relative 'active_record_base'
|
|
31
31
|
require_relative 'hypermesh/version'
|
|
32
32
|
require_relative 'opal/parse_patch'
|
|
33
33
|
require_relative 'opal/set_patches'
|
data/lib/hypermesh/version.rb
CHANGED
|
@@ -60,6 +60,7 @@ module ActiveRecord
|
|
|
60
60
|
# ignore any of these methods if they get called on the client. This list should be trimmed down to include only
|
|
61
61
|
# methods to be called as "macros" such as :after_create, etc...
|
|
62
62
|
SERVER_METHODS = [
|
|
63
|
+
:regulate_relationship, :regulate_scope,
|
|
63
64
|
:attribute_type_decorations, :defined_enums, :_validators, :timestamped_migrations, :lock_optimistically, :lock_optimistically=,
|
|
64
65
|
:local_stored_attributes=, :lock_optimistically?, :attribute_aliases?, :attribute_method_matchers?, :defined_enums?,
|
|
65
66
|
:has_many_without_reactive_record_add_changed_method, :has_many_with_reactive_record_add_changed_method,
|
|
@@ -78,7 +79,7 @@ module ActiveRecord
|
|
|
78
79
|
:_find_callbacks, :_find_callbacks?, :_find_callbacks=, :_touch_callbacks, :_touch_callbacks?, :_touch_callbacks=, :_save_callbacks,
|
|
79
80
|
:_save_callbacks?, :_save_callbacks=, :_create_callbacks, :_create_callbacks?, :_create_callbacks=, :_update_callbacks,
|
|
80
81
|
:_update_callbacks?, :_update_callbacks=, :_destroy_callbacks, :_destroy_callbacks?, :_destroy_callbacks=, :record_timestamps?,
|
|
81
|
-
:
|
|
82
|
+
:pre_synchromesh_scope, :pre_synchromesh_default_scope, :do_not_synchronize, :do_not_synchronize?,
|
|
82
83
|
:logger=, :maintain_test_schema, :maintain_test_schema=, :scope, :time_zone_aware_attributes, :time_zone_aware_attributes=,
|
|
83
84
|
:default_timezone, :default_timezone=, :_attr_readonly, :warn_on_records_fetched_greater_than, :configurations, :configurations=,
|
|
84
85
|
:_attr_readonly?, :table_name_prefix=, :table_name_suffix=, :schema_migrations_table_name=, :internal_metadata_table_name,
|
|
@@ -145,33 +146,107 @@ module ActiveRecord
|
|
|
145
146
|
def method_missing(name, *args, &block)
|
|
146
147
|
if args.count == 1 && name.start_with?("find_by_") && !block
|
|
147
148
|
find_by(name.sub(/^find_by_/, "") => args[0])
|
|
149
|
+
elsif [].respond_to?(name)
|
|
150
|
+
all.send(name, *args, &block)
|
|
151
|
+
elsif name.end_with?('!')
|
|
152
|
+
send(name.chop, *args, &block).send(:reload_from_db) rescue nil
|
|
148
153
|
elsif !SERVER_METHODS.include?(name)
|
|
149
154
|
raise "#{self.name}.#{name}(#{args}) (called class method missing)"
|
|
150
155
|
end
|
|
151
156
|
end
|
|
152
157
|
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
# client side AR
|
|
159
|
+
|
|
160
|
+
# Any method that can be applied to an array will be applied to the result
|
|
161
|
+
# of all instead.
|
|
162
|
+
# Any method ending with ! just means apply the method after forcing a reload
|
|
163
|
+
# from the DB.
|
|
164
|
+
|
|
165
|
+
# alias pre_synchromesh_method_missing method_missing
|
|
166
|
+
#
|
|
167
|
+
# def method_missing(name, *args, &block)
|
|
168
|
+
# return all.send(name, *args, &block) if [].respond_to?(name)
|
|
169
|
+
# if name.end_with?('!')
|
|
170
|
+
# return send(name.chop, *args, &block).send(:reload_from_db) rescue nil
|
|
171
|
+
# end
|
|
172
|
+
# pre_synchromesh_method_missing(name, *args, &block)
|
|
173
|
+
# end
|
|
174
|
+
|
|
175
|
+
def create(*args, &block)
|
|
176
|
+
new(*args).save(&block)
|
|
155
177
|
end
|
|
156
178
|
|
|
157
|
-
def scope(name,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
singleton_class.send(:define_method, "#{name}=") do |collection|
|
|
163
|
-
ReactiveRecord::Base.class_scopes(self)[name] = collection
|
|
179
|
+
def scope(name, *args)
|
|
180
|
+
opts = _synchromesh_scope_args_check(args)
|
|
181
|
+
scope_description = ReactiveRecord::ScopeDescription.new(self, name, opts)
|
|
182
|
+
singleton_class.send(:define_method, name) do |*vargs|
|
|
183
|
+
all.build_child_scope(scope_description, *name, *vargs)
|
|
164
184
|
end
|
|
185
|
+
# singleton_class.send(:define_method, "#{name}=") do |_collection|
|
|
186
|
+
# raise 'NO LONGER IMPLEMENTED - DOESNT PLAY WELL WITH SYNCHROMESH'
|
|
187
|
+
# end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def default_scope(*args, &block)
|
|
191
|
+
opts = _synchromesh_scope_args_check([*block, *args])
|
|
192
|
+
@_default_scopes ||= []
|
|
193
|
+
@_default_scopes << opts
|
|
165
194
|
end
|
|
166
195
|
|
|
167
196
|
def all
|
|
168
|
-
ReactiveRecord::Base.
|
|
197
|
+
ReactiveRecord::Base.default_scope[self] ||=
|
|
198
|
+
begin
|
|
199
|
+
root = ReactiveRecord::Collection
|
|
200
|
+
.new(self, nil, nil, self, 'all')
|
|
201
|
+
.extend(ReactiveRecord::UnscopedCollection)
|
|
202
|
+
(@_default_scopes || [{ client: -> () { true } }]).inject(root) do |scope, opts|
|
|
203
|
+
scope.build_child_scope(ReactiveRecord::ScopeDescription.new(self, :all, opts))
|
|
204
|
+
end
|
|
205
|
+
end
|
|
169
206
|
end
|
|
170
207
|
|
|
171
|
-
def all=(
|
|
172
|
-
|
|
208
|
+
# def all=(_collection)
|
|
209
|
+
# raise "NO LONGER IMPLEMENTED DOESNT PLAY WELL WITH SYNCHROMESH"
|
|
210
|
+
# end
|
|
211
|
+
|
|
212
|
+
def unscoped
|
|
213
|
+
ReactiveRecord::Base.unscoped[self] ||=
|
|
214
|
+
ReactiveRecord::Collection
|
|
215
|
+
.new(self, nil, nil, self, 'unscoped')
|
|
216
|
+
.extend(ReactiveRecord::UnscopedCollection)
|
|
173
217
|
end
|
|
174
218
|
|
|
219
|
+
def finder_method(name)
|
|
220
|
+
ReactiveRecord::ScopeDescription.new(self, "_#{name}", {})
|
|
221
|
+
[name, "#{name}!"].each do |method|
|
|
222
|
+
singleton_class.send(:define_method, method) do |*vargs|
|
|
223
|
+
all.apply_scope("_#{method}", *vargs).first
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def abstract_class=(val)
|
|
229
|
+
@abstract_class = val
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# def scope(name, body)
|
|
233
|
+
# singleton_class.send(:define_method, name) do | *args |
|
|
234
|
+
# args = (args.count == 0) ? name : [name, *args]
|
|
235
|
+
# ReactiveRecord::Base.class_scopes(self)[args] ||= ReactiveRecord::Collection.new(self, nil, nil, self, args)
|
|
236
|
+
# end
|
|
237
|
+
# singleton_class.send(:define_method, "#{name}=") do |collection|
|
|
238
|
+
# ReactiveRecord::Base.class_scopes(self)[name] = collection
|
|
239
|
+
# end
|
|
240
|
+
# end
|
|
241
|
+
|
|
242
|
+
# def all
|
|
243
|
+
# ReactiveRecord::Base.class_scopes(self)[:all] ||= ReactiveRecord::Collection.new(self, nil, nil, self, "all")
|
|
244
|
+
# end
|
|
245
|
+
#
|
|
246
|
+
# def all=(collection)
|
|
247
|
+
# ReactiveRecord::Base.class_scopes(self)[:all] = collection
|
|
248
|
+
# end
|
|
249
|
+
|
|
175
250
|
[:belongs_to, :has_many, :has_one].each do |macro|
|
|
176
251
|
define_method(macro) do |*args| # is this a bug in opal? saying name, scope=nil, opts={} does not work!
|
|
177
252
|
name = args.first
|
|
@@ -116,7 +116,7 @@ module ActiveRecord
|
|
|
116
116
|
# for rails auto generated methods for booleans, remove '?' to get the attribute
|
|
117
117
|
name = name.chop if !is_server_method && is_attribute && name.end_with?('?')
|
|
118
118
|
@backing_record.reactive_get!(name, force_update)
|
|
119
|
-
elsif !block
|
|
119
|
+
elsif !block
|
|
120
120
|
# for rails auto generated methods for booleans, remove '?' to get the attribute
|
|
121
121
|
name = name.chop if !is_server_method && is_attribute && name.end_with?('?')
|
|
122
122
|
@backing_record.reactive_get!([[name]+args], force_update)
|
|
@@ -167,6 +167,20 @@ module ActiveRecord
|
|
|
167
167
|
@backing_record.errors
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
+
def update_attribute(attr, value, &block)
|
|
171
|
+
send("#{attr}=", value)
|
|
172
|
+
save(validate: false, &block)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def update(attrs = {}, &block)
|
|
176
|
+
attrs.each { |attr, value| send("#{attr}=", value) }
|
|
177
|
+
save(&block)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def <=>(other)
|
|
181
|
+
id.to_i <=> other.id.to_i
|
|
182
|
+
end
|
|
183
|
+
|
|
170
184
|
end
|
|
171
185
|
|
|
172
186
|
end
|
|
@@ -87,7 +87,7 @@ module ReactiveRecord
|
|
|
87
87
|
record = @records[model].detect { |record| record.id == id}
|
|
88
88
|
end
|
|
89
89
|
# if we don't have a record then create one
|
|
90
|
-
(record = new(model)).vector = [model, [
|
|
90
|
+
(record = new(model)).vector = [model, [:find_by, attribute => value]] unless record
|
|
91
91
|
# and set the value
|
|
92
92
|
record.sync_attribute(attribute, value)
|
|
93
93
|
# and set the primary if we have one
|
|
@@ -263,7 +263,7 @@ module ReactiveRecord
|
|
|
263
263
|
# nil must not have any children.
|
|
264
264
|
def initialize_collections
|
|
265
265
|
if (!vector || vector.empty?) && id && id != ''
|
|
266
|
-
@vector = [@model, [
|
|
266
|
+
@vector = [@model, [:find_by, @model.primary_key => id]]
|
|
267
267
|
end
|
|
268
268
|
@model.reflect_on_all_associations.each do |assoc|
|
|
269
269
|
if assoc.collection? && attributes[assoc.attribute].nil?
|
|
@@ -252,7 +252,7 @@ module ReactiveRecord
|
|
|
252
252
|
elsif filter?
|
|
253
253
|
@collection = filter_records(@parent.collection)
|
|
254
254
|
end
|
|
255
|
-
elsif @parent.count.zero?
|
|
255
|
+
elsif @parent._count_internal(false).zero? # just changed this from count.zero?
|
|
256
256
|
@count = 0
|
|
257
257
|
end
|
|
258
258
|
end
|
|
@@ -289,18 +289,26 @@ module ReactiveRecord
|
|
|
289
289
|
@count = val
|
|
290
290
|
end
|
|
291
291
|
|
|
292
|
-
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _count_internal(load_from_client)
|
|
295
|
+
# when count is called on a leaf, count_internal is called for each
|
|
296
|
+
# ancestor. Only the outermost count has load_from_client == true
|
|
293
297
|
observed
|
|
294
298
|
if @collection
|
|
295
299
|
@collection.count
|
|
296
300
|
elsif @count ||= ReactiveRecord::Base.fetch_from_db([*@vector, "*count"])
|
|
297
301
|
@count
|
|
298
302
|
else
|
|
299
|
-
ReactiveRecord::Base.load_from_db(nil, *@vector, "*count")
|
|
303
|
+
ReactiveRecord::Base.load_from_db(nil, *@vector, "*count") if load_from_client
|
|
300
304
|
@count = 1
|
|
301
305
|
end
|
|
302
306
|
end
|
|
303
307
|
|
|
308
|
+
def count
|
|
309
|
+
_count_internal(true)
|
|
310
|
+
end
|
|
311
|
+
|
|
304
312
|
alias_method :length, :count
|
|
305
313
|
|
|
306
314
|
# WHY IS THIS NEEDED? Perhaps it was just for debug
|
|
@@ -473,15 +481,19 @@ module ReactiveRecord
|
|
|
473
481
|
@dummy_collection.loading?
|
|
474
482
|
end
|
|
475
483
|
|
|
476
|
-
def empty?
|
|
484
|
+
def empty?
|
|
485
|
+
# should be handled by method missing below, but opal-rspec does not deal well
|
|
486
|
+
# with method missing, so to test...
|
|
477
487
|
all.empty?
|
|
478
488
|
end
|
|
479
489
|
|
|
480
490
|
def method_missing(method, *args, &block)
|
|
481
491
|
if [].respond_to? method
|
|
482
492
|
all.send(method, *args, &block)
|
|
483
|
-
elsif ScopeDescription.find(@target_klass, method)
|
|
493
|
+
elsif ScopeDescription.find(@target_klass, method)
|
|
484
494
|
apply_scope(method, *args)
|
|
495
|
+
elsif args.count == 1 && method.start_with?('find_by_')
|
|
496
|
+
apply_scope(:find_by, method.sub(/^find_by_/, '') => args.first)
|
|
485
497
|
elsif @target_klass.respond_to?(method) && ScopeDescription.find(@target_klass, "_#{method}")
|
|
486
498
|
apply_scope("_#{method}", *args).first
|
|
487
499
|
else
|
|
@@ -133,6 +133,7 @@ module ReactiveRecord
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def self.schedule_fetch
|
|
136
|
+
React::State.set_state(WhileLoading, :quiet, false) # moved from while loading module see loading! method
|
|
136
137
|
@fetch_scheduled ||= after(0) do
|
|
137
138
|
if @pending_fetches.count > 0 # during testing we might reset the context while there are pending fetches otherwise this would never normally happen
|
|
138
139
|
last_fetch_at = @last_fetch_at
|
|
@@ -143,23 +144,29 @@ module ReactiveRecord
|
|
|
143
144
|
start_time = `Date.now()`
|
|
144
145
|
Operations::Fetch.run(models: models, associations: associations, pending_fetches: pending_fetches)
|
|
145
146
|
.then do |response|
|
|
146
|
-
fetch_time = `Date.now()`
|
|
147
|
-
log(" Fetched in: #{`(fetch_time - start_time)/ 1000`}s")
|
|
148
147
|
begin
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
fetch_time = `Date.now()`
|
|
149
|
+
log(" Fetched in: #{`(fetch_time - start_time)/ 1000`}s")
|
|
150
|
+
begin
|
|
151
|
+
ReactiveRecord::Base.load_from_json(response)
|
|
152
|
+
rescue Exception => e
|
|
153
|
+
log("Unexpected exception raised while loading json from server: #{e}", :error)
|
|
154
|
+
end
|
|
155
|
+
log(" Processed in: #{`(Date.now() - fetch_time) / 1000`}s")
|
|
156
|
+
log([" Returned: %o", response.to_n])
|
|
157
|
+
ReactiveRecord.run_blocks_to_load last_fetch_at
|
|
158
|
+
ensure
|
|
159
|
+
ReactiveRecord::WhileLoading.loaded_at last_fetch_at
|
|
160
|
+
ReactiveRecord::WhileLoading.quiet! if @pending_fetches.empty?
|
|
152
161
|
end
|
|
153
|
-
log(" Processed in: #{`(Date.now() - fetch_time) / 1000`}s")
|
|
154
|
-
log([" Returned: %o", response.to_n])
|
|
155
|
-
ReactiveRecord.run_blocks_to_load last_fetch_at
|
|
156
|
-
ReactiveRecord::WhileLoading.loaded_at last_fetch_at
|
|
157
|
-
ReactiveRecord::WhileLoading.quiet! if @pending_fetches.empty?
|
|
158
162
|
end
|
|
159
163
|
.fail do |response|
|
|
160
164
|
log("Fetch failed", :error)
|
|
161
|
-
|
|
162
|
-
|
|
165
|
+
begin
|
|
166
|
+
ReactiveRecord.run_blocks_to_load(last_fetch_at, response)
|
|
167
|
+
ensure
|
|
168
|
+
ReactiveRecord::WhileLoading.quiet! if @pending_fetches.empty?
|
|
169
|
+
end
|
|
163
170
|
end
|
|
164
171
|
@pending_fetches = []
|
|
165
172
|
@pending_records = []
|
|
@@ -123,7 +123,8 @@ module ReactiveRecord
|
|
|
123
123
|
def loading!
|
|
124
124
|
React::RenderingContext.waiting_on_resources = true
|
|
125
125
|
React::State.get_state(self, :loaded_at)
|
|
126
|
-
|
|
126
|
+
# this was moved to where the fetch is actually pushed on to the fetch array in isomorphic base
|
|
127
|
+
# React::State.set_state(self, :quiet, false)
|
|
127
128
|
@is_loading = true
|
|
128
129
|
end
|
|
129
130
|
|
|
@@ -298,7 +299,7 @@ if RUBY_ENGINE == 'opal'
|
|
|
298
299
|
def reactive_record_link_set_while_loading_container_class
|
|
299
300
|
node = dom_node
|
|
300
301
|
loading = (waiting_on_resources ? `true` : `false`)
|
|
301
|
-
%x{
|
|
302
|
+
%x{
|
|
302
303
|
if (typeof node === "undefined" || node === null) return;
|
|
303
304
|
var while_loading_container_id = node.getAttribute('data-reactive_record_while_loading_container_id');
|
|
304
305
|
if (#{!self.is_a?(ReactiveRecord::WhileLoading)} && while_loading_container_id !== null && while_loading_container_id !== "") {
|
|
@@ -203,29 +203,36 @@ module ReactiveRecord
|
|
|
203
203
|
def apply_method_to_cache(method)
|
|
204
204
|
@db_cache.inject(nil) do |representative, cache_item|
|
|
205
205
|
if cache_item.vector == vector
|
|
206
|
-
# TODO: Security - this is the wrong check in the wrong place...
|
|
207
|
-
if @value.class < ActiveRecord::Base and @value.attributes.has_key?(method) # TODO: second check is not needed, its built into check_permmissions, check should be does class respond to check_permissions...
|
|
208
|
-
@value.check_permission_with_acting_user(@acting_user, :view_permitted?, method)
|
|
209
|
-
end
|
|
210
206
|
if method == "*"
|
|
207
|
+
# apply_star does the security check if value is present
|
|
211
208
|
cache_item.apply_star || representative
|
|
212
209
|
elsif method == "*all"
|
|
213
|
-
|
|
210
|
+
# if we secure the collection then we assume its okay to read the ids
|
|
211
|
+
cache_item.build_new_cache_item(cache_item.value.__secure_collection_check(@acting_user).collect { |record| record.id }, method, method)
|
|
214
212
|
elsif method == "*count"
|
|
215
|
-
cache_item.build_new_cache_item(cache_item.value.count, method, method)
|
|
213
|
+
cache_item.build_new_cache_item(cache_item.value.__secure_collection_check(@acting_user).count, method, method)
|
|
216
214
|
elsif preloaded_value = @preloaded_records[cache_item.absolute_vector + [method]]
|
|
215
|
+
# no security check needed since we already evaluated this
|
|
217
216
|
cache_item.build_new_cache_item(preloaded_value, method, method)
|
|
218
217
|
elsif aggregation = cache_item.aggregation?(method)
|
|
218
|
+
# aggregations are not protected
|
|
219
219
|
cache_item.build_new_cache_item(aggregation.mapping.collect { |attribute, accessor| cache_item.value[attribute] }, method, method)
|
|
220
220
|
else
|
|
221
|
-
if !cache_item.value || cache_item.value.
|
|
221
|
+
if !cache_item.value || cache_item.value.is_a?(Array)
|
|
222
|
+
# seeing as we just returning representative, no check is needed (its already checked)
|
|
222
223
|
representative
|
|
223
224
|
else
|
|
224
|
-
# TODO: Security. Protect the send(*method). But its complicated.. method can be an attribute, scope, relationship or actual method.
|
|
225
|
-
# Each needs some protection logic.
|
|
226
225
|
begin
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
secured_method = "__secure_remote_access_to_#{[*method].first}"
|
|
227
|
+
if @value.class < ActiveRecord::Base and @value.attributes.has_key?(method) # TODO: second check is not needed, its built into check_permmissions, check should be does class respond to check_permissions...
|
|
228
|
+
@value.check_permission_with_acting_user(@acting_user, :view_permitted?, method)
|
|
229
|
+
cache_item.build_new_cache_item(cache_item.value.send(*method), method, method)
|
|
230
|
+
elsif cache_item.value.respond_to? secured_method
|
|
231
|
+
cache_item.build_new_cache_item(cache_item.value.send(secured_method, @acting_user, *([*method][1..-1])), method, method)
|
|
232
|
+
else
|
|
233
|
+
raise "method missing"
|
|
234
|
+
end
|
|
235
|
+
rescue Exception => e # this check may no longer be needed as we are quite explicit now on which methods we apply
|
|
229
236
|
# ReactiveRecord::Pry::rescued(e)
|
|
230
237
|
::Rails.logger.debug "\033[0;31;1mERROR: HyperModel exception caught when applying #{method} to db object #{cache_item.value}: #{e}\033[0;30;21m"
|
|
231
238
|
raise e, "HyperModel fetching records failed, exception caught when applying #{method} to db object #{cache_item.value}: #{e}", e.backtrace
|
|
@@ -248,9 +255,9 @@ module ReactiveRecord
|
|
|
248
255
|
end
|
|
249
256
|
end
|
|
250
257
|
|
|
251
|
-
# SECURITY - SAFE
|
|
258
|
+
# SECURITY - NOW SAFE
|
|
252
259
|
def apply_star
|
|
253
|
-
if @value && @value.length > 0
|
|
260
|
+
if @value && @value.__secure_collection_check(@acting_user) && @value.length > 0
|
|
254
261
|
i = -1
|
|
255
262
|
@value.inject(nil) do |representative, current_value|
|
|
256
263
|
i += 1
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hyper-mesh
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.0.
|
|
4
|
+
version: 1.0.0.lap24
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mitch VanDuyn
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2018-02-
|
|
12
|
+
date: 2018-02-28 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activerecord
|
|
@@ -31,28 +31,28 @@ dependencies:
|
|
|
31
31
|
requirements:
|
|
32
32
|
- - '='
|
|
33
33
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: 1.0.0.
|
|
34
|
+
version: 1.0.0.lap24
|
|
35
35
|
type: :runtime
|
|
36
36
|
prerelease: false
|
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
|
38
38
|
requirements:
|
|
39
39
|
- - '='
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
|
-
version: 1.0.0.
|
|
41
|
+
version: 1.0.0.lap24
|
|
42
42
|
- !ruby/object:Gem::Dependency
|
|
43
43
|
name: hyper-operation
|
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
|
45
45
|
requirements:
|
|
46
46
|
- - '='
|
|
47
47
|
- !ruby/object:Gem::Version
|
|
48
|
-
version: 1.0.0.
|
|
48
|
+
version: 1.0.0.lap24
|
|
49
49
|
type: :runtime
|
|
50
50
|
prerelease: false
|
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
|
52
52
|
requirements:
|
|
53
53
|
- - '='
|
|
54
54
|
- !ruby/object:Gem::Version
|
|
55
|
-
version: 1.0.0.
|
|
55
|
+
version: 1.0.0.lap24
|
|
56
56
|
- !ruby/object:Gem::Dependency
|
|
57
57
|
name: bundler
|
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -129,14 +129,14 @@ dependencies:
|
|
|
129
129
|
requirements:
|
|
130
130
|
- - '='
|
|
131
131
|
- !ruby/object:Gem::Version
|
|
132
|
-
version: 1.0.0.
|
|
132
|
+
version: 1.0.0.lap24
|
|
133
133
|
type: :development
|
|
134
134
|
prerelease: false
|
|
135
135
|
version_requirements: !ruby/object:Gem::Requirement
|
|
136
136
|
requirements:
|
|
137
137
|
- - '='
|
|
138
138
|
- !ruby/object:Gem::Version
|
|
139
|
-
version: 1.0.0.
|
|
139
|
+
version: 1.0.0.lap24
|
|
140
140
|
- !ruby/object:Gem::Dependency
|
|
141
141
|
name: hyper-trace
|
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -549,6 +549,20 @@ dependencies:
|
|
|
549
549
|
- - ">="
|
|
550
550
|
- !ruby/object:Gem::Version
|
|
551
551
|
version: '0'
|
|
552
|
+
- !ruby/object:Gem::Dependency
|
|
553
|
+
name: pry-rescue
|
|
554
|
+
requirement: !ruby/object:Gem::Requirement
|
|
555
|
+
requirements:
|
|
556
|
+
- - ">="
|
|
557
|
+
- !ruby/object:Gem::Version
|
|
558
|
+
version: '0'
|
|
559
|
+
type: :development
|
|
560
|
+
prerelease: false
|
|
561
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
562
|
+
requirements:
|
|
563
|
+
- - ">="
|
|
564
|
+
- !ruby/object:Gem::Version
|
|
565
|
+
version: '0'
|
|
552
566
|
description: HyperMesh is the base for HyperModel. HyperModel gives your HyperComponents
|
|
553
567
|
CRUD access to your ActiveRecord models on the client, using the the standard ActiveRecord
|
|
554
568
|
API. HyperModel also implements push notifications (via a number of possible technologies)
|
|
@@ -622,8 +636,7 @@ homepage: http://ruby-hyperloop.org
|
|
|
622
636
|
licenses:
|
|
623
637
|
- MIT
|
|
624
638
|
metadata: {}
|
|
625
|
-
post_install_message:
|
|
626
|
-
known security issues! Not recommended for production use!\e[0;30;21m"
|
|
639
|
+
post_install_message:
|
|
627
640
|
rdoc_options: []
|
|
628
641
|
require_paths:
|
|
629
642
|
- lib
|