hyper-model 1.0.alpha1.1 → 1.0.alpha1.6
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/.gitignore +4 -1
- data/.rspec +0 -1
- data/Gemfile +6 -6
- data/Rakefile +27 -3
- data/hyper-model.gemspec +10 -19
- data/lib/active_record_base.rb +101 -33
- data/lib/hyper-model.rb +4 -2
- data/lib/hyper_model/version.rb +1 -1
- data/lib/hyper_react/input_tags.rb +2 -1
- data/lib/reactive_record/active_record/associations.rb +130 -34
- data/lib/reactive_record/active_record/base.rb +17 -0
- data/lib/reactive_record/active_record/class_methods.rb +124 -52
- data/lib/reactive_record/active_record/error.rb +2 -0
- data/lib/reactive_record/active_record/errors.rb +10 -6
- data/lib/reactive_record/active_record/instance_methods.rb +74 -6
- data/lib/reactive_record/active_record/reactive_record/backing_record_inspector.rb +22 -5
- data/lib/reactive_record/active_record/reactive_record/base.rb +56 -30
- data/lib/reactive_record/active_record/reactive_record/collection.rb +219 -70
- data/lib/reactive_record/active_record/reactive_record/dummy_polymorph.rb +22 -0
- data/lib/reactive_record/active_record/reactive_record/dummy_value.rb +27 -15
- data/lib/reactive_record/active_record/reactive_record/getters.rb +33 -10
- data/lib/reactive_record/active_record/reactive_record/isomorphic_base.rb +73 -46
- data/lib/reactive_record/active_record/reactive_record/lookup_tables.rb +5 -5
- data/lib/reactive_record/active_record/reactive_record/operations.rb +10 -3
- data/lib/reactive_record/active_record/reactive_record/scoped_collection.rb +3 -0
- data/lib/reactive_record/active_record/reactive_record/setters.rb +108 -71
- data/lib/reactive_record/active_record/reactive_record/while_loading.rb +258 -41
- data/lib/reactive_record/broadcast.rb +62 -25
- data/lib/reactive_record/interval.rb +3 -3
- data/lib/reactive_record/permissions.rb +14 -2
- data/lib/reactive_record/scope_description.rb +3 -2
- data/lib/reactive_record/server_data_cache.rb +99 -49
- data/polymorph-notes.md +143 -0
- data/spec_fails.txt +3 -0
- metadata +49 -162
- data/Gemfile.lock +0 -431
@@ -14,15 +14,15 @@ module ReactiveRecord
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def waiting_for_save(model)
|
17
|
-
@waiting_for_save[model]
|
17
|
+
@waiting_for_save[model.base_class]
|
18
18
|
end
|
19
19
|
|
20
20
|
def wait_for_save(model, &block)
|
21
|
-
@waiting_for_save[model] << block
|
21
|
+
@waiting_for_save[model.base_class] << block
|
22
22
|
end
|
23
23
|
|
24
24
|
def clear_waiting_for_save(model)
|
25
|
-
@waiting_for_save[model] = []
|
25
|
+
@waiting_for_save[model.base_class] = []
|
26
26
|
end
|
27
27
|
|
28
28
|
def lookup_by_object_id(object_id)
|
@@ -33,8 +33,8 @@ module ReactiveRecord
|
|
33
33
|
`#{@records_by_object_id}[#{record.object_id}] = #{record}`
|
34
34
|
end
|
35
35
|
|
36
|
-
def lookup_by_id(
|
37
|
-
`#{@records_by_id}[#{
|
36
|
+
def lookup_by_id(model, id) # model and id
|
37
|
+
`#{@records_by_id}[#{[model.base_class, id]}]` || nil
|
38
38
|
end
|
39
39
|
|
40
40
|
def set_id_lookup(record)
|
@@ -12,6 +12,10 @@ module ReactiveRecord
|
|
12
12
|
|
13
13
|
FORMAT = '0x%x'
|
14
14
|
|
15
|
+
class << self
|
16
|
+
attr_accessor :last_response_sent_at
|
17
|
+
end
|
18
|
+
|
15
19
|
def self.serialize_params(hash)
|
16
20
|
hash['associations'].each do |assoc|
|
17
21
|
assoc['parent_id'] = FORMAT % assoc['parent_id']
|
@@ -38,6 +42,7 @@ module ReactiveRecord
|
|
38
42
|
response[:saved_models].each do |saved_model|
|
39
43
|
saved_model[0] = FORMAT % saved_model[0]
|
40
44
|
end if response.is_a?(Hash) && response[:saved_models]
|
45
|
+
response[:sent_at] = Time.now.to_f
|
41
46
|
response
|
42
47
|
end
|
43
48
|
|
@@ -45,6 +50,7 @@ module ReactiveRecord
|
|
45
50
|
response[:saved_models].each do |saved_model|
|
46
51
|
saved_model[0] = saved_model[0].to_i(16)
|
47
52
|
end if response.is_a?(Hash) && response[:saved_models]
|
53
|
+
Base.last_response_sent_at = response.delete(:sent_at)
|
48
54
|
response
|
49
55
|
end
|
50
56
|
end
|
@@ -65,8 +71,9 @@ module ReactiveRecord
|
|
65
71
|
]
|
66
72
|
end
|
67
73
|
failed do |e|
|
68
|
-
|
69
|
-
|
74
|
+
e.define_singleton_method(:__hyperstack_on_error) do |operation, params, fmted_message|
|
75
|
+
Hyperstack.on_error(operation, self, params, fmted_message)
|
76
|
+
end
|
70
77
|
raise e
|
71
78
|
end
|
72
79
|
end
|
@@ -92,7 +99,7 @@ module ReactiveRecord
|
|
92
99
|
class Destroy < Base
|
93
100
|
param :acting_user, nils: true
|
94
101
|
param :model
|
95
|
-
param :id
|
102
|
+
param :id, nils: true
|
96
103
|
param :vector
|
97
104
|
step do
|
98
105
|
ReactiveRecord::Base.destroy_record(
|
@@ -9,7 +9,10 @@ module ReactiveRecord
|
|
9
9
|
def set_pre_sync_related_records(related_records, _record = nil)
|
10
10
|
@pre_sync_related_records = nil
|
11
11
|
ReactiveRecord::Base.catch_db_requests do
|
12
|
+
# puts "#{self}.set_pre_sync_related_records filter_records(#{related_records})"
|
13
|
+
|
12
14
|
@pre_sync_related_records = filter_records(related_records)
|
15
|
+
# puts "returns #{@pre_sync_related_records}"
|
13
16
|
live_scopes.each do |scope|
|
14
17
|
scope.set_pre_sync_related_records(@pre_sync_related_records)
|
15
18
|
end
|
@@ -43,17 +43,26 @@ module ReactiveRecord
|
|
43
43
|
|
44
44
|
def set_belongs_to(assoc, raw_value)
|
45
45
|
set_common(assoc.attribute, raw_value) do |value, attr|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# itself will just reactively read the value (a model instance) by doing a .id
|
53
|
-
update_belongs_to attr, value.itself
|
46
|
+
current_value = @attributes[assoc.attribute]
|
47
|
+
update_has_many_through_associations assoc, nil, current_value, :remove_member
|
48
|
+
update_has_many_through_associations assoc, nil, value, :add_member
|
49
|
+
remove_current_inverse_attribute assoc, nil, current_value
|
50
|
+
add_new_inverse_attribute assoc, nil, value
|
51
|
+
update_belongs_to attr, value.itself
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
55
|
+
def set_belongs_to_via_has_many(orig, value)
|
56
|
+
assoc = orig.inverse
|
57
|
+
attr = assoc.attribute
|
58
|
+
current_value = @attributes[attr]
|
59
|
+
update_has_many_through_associations assoc, orig, current_value, :remove_member
|
60
|
+
update_has_many_through_associations assoc, orig, value, :add_member
|
61
|
+
remove_current_inverse_attribute assoc, orig, current_value
|
62
|
+
add_new_inverse_attribute assoc, orig, value
|
63
|
+
update_belongs_to attr, value.itself
|
64
|
+
end
|
65
|
+
|
57
66
|
def sync_has_many(attr)
|
58
67
|
set_change_status_and_notify_only attr, @attributes[attr] != @synced_attributes[attr]
|
59
68
|
end
|
@@ -85,30 +94,28 @@ module ReactiveRecord
|
|
85
94
|
end
|
86
95
|
|
87
96
|
def set_attribute_change_status_and_notify(attr, changed, new_value)
|
88
|
-
if @virgin
|
97
|
+
if @virgin || @being_destroyed
|
89
98
|
@attributes[attr] = new_value
|
90
99
|
else
|
91
100
|
change_status_and_notify_helper(attr, changed) do |had_key, current_value|
|
92
101
|
@attributes[attr] = new_value
|
93
102
|
if !data_loading? ||
|
94
103
|
(on_opal_client? && had_key && current_value.loaded? && current_value != new_value)
|
95
|
-
Hyperstack::Internal::
|
104
|
+
Hyperstack::Internal::State::Variable.set(self, attr, new_value, data_loading?)
|
96
105
|
end
|
97
106
|
end
|
98
107
|
end
|
99
108
|
end
|
100
109
|
|
101
110
|
def set_change_status_and_notify_only(attr, changed)
|
102
|
-
return if @virgin
|
111
|
+
return if @virgin || @being_destroyed
|
103
112
|
change_status_and_notify_helper(attr, changed) do
|
104
|
-
Hyperstack::Internal::
|
113
|
+
Hyperstack::Internal::State::Variable.set(self, attr, nil) unless data_loading?
|
105
114
|
end
|
106
115
|
end
|
107
116
|
|
108
117
|
def change_status_and_notify_helper(attr, changed)
|
109
118
|
empty_before = changed_attributes.empty?
|
110
|
-
# TODO: confirm this works:
|
111
|
-
# || data_loading? added so that model.new can be wrapped in a ReactiveRecord.load_data
|
112
119
|
if !changed || data_loading?
|
113
120
|
changed_attributes.delete(attr)
|
114
121
|
elsif !changed_attributes.include?(attr)
|
@@ -117,7 +124,7 @@ module ReactiveRecord
|
|
117
124
|
yield @attributes.key?(attr), @attributes[attr]
|
118
125
|
return unless empty_before != changed_attributes.empty?
|
119
126
|
if on_opal_client? && !data_loading?
|
120
|
-
Hyperstack::Internal::
|
127
|
+
Hyperstack::Internal::State::Variable.set(self, '!CHANGED!', !changed_attributes.empty?, true)
|
121
128
|
end
|
122
129
|
return unless aggregate_owner
|
123
130
|
aggregate_owner.set_change_status_and_notify_only(
|
@@ -125,70 +132,100 @@ module ReactiveRecord
|
|
125
132
|
)
|
126
133
|
end
|
127
134
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
current_value.attributes[inverse_attr].delete(@ar_instance) unless current_value.nil?
|
135
|
+
# when updating the inverse attribute of a belongs_to that is itself a belongs_to
|
136
|
+
# (i.e. 1-1 relationship) we clear the existing inverse value and then
|
137
|
+
# write the current record to the new value
|
138
|
+
|
139
|
+
# when updating an inverse attribute of a belongs_to that is a has_many (i.e. a collection)
|
140
|
+
# we need to first remove the current associated value (if non-nil), then add the new
|
141
|
+
# value to the collection. If the inverse collection is not yet initialized we do it here.
|
142
|
+
|
143
|
+
# the above is split into three methods, because the inverse of apolymorphic belongs to may
|
144
|
+
# change from has_one to has_many. So we first deal with the current value, then
|
145
|
+
# update the new value which uses the push_onto_collection helper
|
146
|
+
|
147
|
+
def remove_current_inverse_attribute(association, orig, model)
|
148
|
+
return if model.nil?
|
149
|
+
inverse_association = association.inverse(model)
|
150
|
+
return if inverse_association == orig
|
151
|
+
if inverse_association.collection?
|
152
|
+
# note we don't have to check if the collection exists, since it must
|
153
|
+
# exist as at this ar_instance is already part of it.
|
154
|
+
model.attributes[inverse_association.attribute].delete(@ar_instance)
|
149
155
|
else
|
150
|
-
|
156
|
+
model.attributes[inverse_association.attribute] = nil
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def add_new_inverse_attribute(association, orig, model)
|
161
|
+
return if model.nil?
|
162
|
+
inverse_association = association.inverse(model)
|
163
|
+
return if inverse_association == orig
|
164
|
+
if inverse_association.collection?
|
165
|
+
model.backing_record.push_onto_collection(@model, inverse_association, @ar_instance)
|
166
|
+
else
|
167
|
+
inverse_attr = inverse_association.attribute
|
168
|
+
model.attributes[inverse_attr] = @ar_instance
|
169
|
+
return if data_loading?
|
170
|
+
Hyperstack::Internal::State::Variable.set(model.backing_record, inverse_attr, @ar_instance)
|
151
171
|
end
|
152
172
|
end
|
153
173
|
|
154
174
|
def push_onto_collection(model, association, ar_instance)
|
155
175
|
@attributes[association.attribute] ||= Collection.new(model, @ar_instance, association)
|
156
|
-
@attributes[association.attribute]
|
176
|
+
@attributes[association.attribute]._internal_push ar_instance
|
177
|
+
end
|
178
|
+
|
179
|
+
# class Membership < ActiveRecord::Base
|
180
|
+
# belongs_to :uzer
|
181
|
+
# belongs_to :memerable, polymorphic: true
|
182
|
+
# end
|
183
|
+
#
|
184
|
+
# class Project < ActiveRecord::Base
|
185
|
+
# has_many :memberships, as: :memerable, dependent: :destroy
|
186
|
+
# has_many :uzers, through: :memberships
|
187
|
+
# end
|
188
|
+
#
|
189
|
+
# class Group < ActiveRecord::Base
|
190
|
+
# has_many :memberships, as: :memerable, dependent: :destroy
|
191
|
+
# has_many :uzers, through: :memberships
|
192
|
+
# end
|
193
|
+
#
|
194
|
+
# class Uzer < ActiveRecord::Base
|
195
|
+
# has_many :memberships
|
196
|
+
# has_many :groups, through: :memberships, source: :memerable, source_type: 'Group'
|
197
|
+
# has_many :projects, through: :memberships, source: :memerable, source_type: 'Project'
|
198
|
+
# end
|
199
|
+
|
200
|
+
# membership.uzer = some_new_uzer (i.e. through association is changing)
|
201
|
+
# means membership.some_new_uzer.(groups OR projects) << uzer.memberable (depending on type of memberable)
|
202
|
+
# and we have to remove the current value of the source association (memerable) from the current uzer group or project
|
203
|
+
# and we have to then find any inverse has_many_through association (i.e. group or projects.uzers) and delete the
|
204
|
+
# current value from those collections and push the new value on
|
205
|
+
|
206
|
+
def update_has_many_through_associations(assoc, orig, value, method)
|
207
|
+
return if value.nil?
|
208
|
+
assoc.through_associations(value).each do |ta|
|
209
|
+
next if orig == ta
|
210
|
+
source_value = @attributes[ta.source]
|
211
|
+
# skip if source value is nil or if type of the association does not match type of source
|
212
|
+
next unless source_value.class.to_s == ta.source_type
|
213
|
+
ta.send method, source_value, value
|
214
|
+
ta.source_associations(source_value).each do |sa|
|
215
|
+
sa.send method, value, source_value
|
216
|
+
end
|
217
|
+
end
|
157
218
|
end
|
158
219
|
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
end
|
220
|
+
# def remove_src_assoc(sa, source_value, current_value)
|
221
|
+
# source_inverse_collection = source_value.attributes[sa.attribute]
|
222
|
+
# source_inverse_collection.delete(current_value) if source_inverse_collection
|
223
|
+
# end
|
224
|
+
#
|
225
|
+
# def add_src_assoc(sa, source_value, new_value)
|
226
|
+
# source_value.attributes[sa.attribute] ||= Collection.new(sa.owner_class, source_value, sa)
|
227
|
+
# source_value.attributes[sa.attribute] << new_value
|
228
|
+
# end
|
163
229
|
|
164
|
-
def update_through_association(ta, new_belongs_to_value)
|
165
|
-
# appointment.doctor = doctor_new_value (i.e. through association is changing)
|
166
|
-
# means appointment.doctor_new_value.patients << appointment.patient
|
167
|
-
# and we have to appointment.doctor_current_value.patients.delete(appointment.patient)
|
168
|
-
source_value = @attributes[ta.source]
|
169
|
-
current_belongs_to_value = @attributes[ta.inverse.attribute]
|
170
|
-
return unless source_value
|
171
|
-
unless current_belongs_to_value.nil? || current_belongs_to_value.attributes[ta.attribute].nil?
|
172
|
-
current_belongs_to_value.attributes[ta.attribute].delete(source_value)
|
173
|
-
end
|
174
|
-
return unless new_belongs_to_value
|
175
|
-
new_belongs_to_value.attributes[ta.attribute] ||= Collection.new(ta.klass, new_belongs_to_value, ta)
|
176
|
-
new_belongs_to_value.attributes[ta.attribute] << source_value
|
177
|
-
end
|
178
|
-
|
179
|
-
def update_source_association(sa, new_source_value)
|
180
|
-
# appointment.patient = patient_value (i.e. source is changing)
|
181
|
-
# means appointment.doctor.patients.delete(appointment.patient)
|
182
|
-
# means appointment.doctor.patients << patient_value
|
183
|
-
belongs_to_value = @attributes[sa.inverse.attribute]
|
184
|
-
current_source_value = @attributes[sa.source]
|
185
|
-
return unless belongs_to_value
|
186
|
-
unless belongs_to_value.attributes[sa.attribute].nil? || current_source_value.nil?
|
187
|
-
belongs_to_value.attributes[sa.attribute].delete(current_source_value)
|
188
|
-
end
|
189
|
-
return unless new_source_value
|
190
|
-
belongs_to_value.attributes[sa.attribute] ||= Collection.new(sa.klass, belongs_to_value, sa)
|
191
|
-
belongs_to_value.attributes[sa.attribute] << new_source_value
|
192
|
-
end
|
193
230
|
end
|
194
231
|
end
|
@@ -74,6 +74,191 @@ module ReactiveRecord
|
|
74
74
|
# To notify React that something is loading use React::WhileLoading.loading!
|
75
75
|
# once everything is loaded then do React::WhileLoading.loaded_at message (typically a time stamp just for debug purposes)
|
76
76
|
|
77
|
+
# class WhileLoading
|
78
|
+
#
|
79
|
+
# include Hyperstack::Component::IsomorphicHelpers
|
80
|
+
#
|
81
|
+
# before_first_mount do
|
82
|
+
# @css_to_preload = ""
|
83
|
+
# @while_loading_counter = 0
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# def self.get_next_while_loading_counter
|
87
|
+
# @while_loading_counter += 1
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# def self.preload_css(css)
|
91
|
+
# @css_to_preload += "#{css}\n"
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# def self.has_observers?
|
95
|
+
# Hyperstack::Internal::State::Variable.observed?(self, :loaded_at)
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# class << self
|
99
|
+
# alias :observed? :has_observers?
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# prerender_footer do
|
103
|
+
# "<style>\n#{@css_to_preload}\n</style>".tap { @css_to_preload = ""}
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# if RUBY_ENGINE == 'opal'
|
107
|
+
#
|
108
|
+
# # +: I DONT THINK WE USE opal-jquery in this module anymore - require 'opal-jquery' if opal_client?
|
109
|
+
# # -: You think wrong. add_style_sheet uses the jQuery $, after_mount too, others too
|
110
|
+
# # -: I removed those references. Now you think right.
|
111
|
+
#
|
112
|
+
# include Hyperstack::Component
|
113
|
+
#
|
114
|
+
# param :render_original_child
|
115
|
+
# param :loading
|
116
|
+
#
|
117
|
+
# class << self
|
118
|
+
#
|
119
|
+
# def loading?
|
120
|
+
# @is_loading
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# def loading!
|
124
|
+
# Hyperstack::Internal::Component::RenderingContext.waiting_on_resources = true
|
125
|
+
# Hyperstack::Internal::State::Variable.get(self, :loaded_at)
|
126
|
+
# # this was moved to where the fetch is actually pushed on to the fetch array in isomorphic base
|
127
|
+
# # Hyperstack::Internal::State::Variable.set(self, :quiet, false)
|
128
|
+
# @is_loading = true
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# def loaded_at(loaded_at)
|
132
|
+
# Hyperstack::Internal::State::Variable.set(self, :loaded_at, loaded_at)
|
133
|
+
# @is_loading = false
|
134
|
+
# end
|
135
|
+
#
|
136
|
+
# def quiet?
|
137
|
+
# Hyperstack::Internal::State::Variable.get(self, :quiet)
|
138
|
+
# end
|
139
|
+
#
|
140
|
+
# def page_loaded?
|
141
|
+
# Hyperstack::Internal::State::Variable.get(self, :page_loaded)
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# def quiet!
|
145
|
+
# Hyperstack::Internal::State::Variable.set(self, :quiet, true)
|
146
|
+
# after(1) { Hyperstack::Internal::State::Variable.set(self, :page_loaded, true) } unless on_opal_server? or @page_loaded
|
147
|
+
# @page_loaded = true
|
148
|
+
# end
|
149
|
+
#
|
150
|
+
# def add_style_sheet
|
151
|
+
# # directly assigning the code to the variable triggers a opal 0.10.5 compiler bug.
|
152
|
+
# unless @style_sheet_added
|
153
|
+
# %x{
|
154
|
+
# var style_el = document.createElement("style");
|
155
|
+
# style_el.setAttribute("type", "text/css");
|
156
|
+
# style_el.innerHTML = ".reactive_record_is_loading > .reactive_record_show_when_loaded { display: none !important; }\n" +
|
157
|
+
# ".reactive_record_is_loaded > .reactive_record_show_while_loading { display: none !important; }";
|
158
|
+
# document.head.append(style_el);
|
159
|
+
# }
|
160
|
+
# @style_sheet_added = true
|
161
|
+
# end
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# end
|
165
|
+
#
|
166
|
+
# def after_mount_and_update
|
167
|
+
# @waiting_on_resources = @Loading
|
168
|
+
# node = dom_node
|
169
|
+
# %x{
|
170
|
+
# Array.from(node.children).forEach(
|
171
|
+
# function(current_node, current_index, list_obj) {
|
172
|
+
# if (current_index > 0 && current_node.className.indexOf('reactive_record_show_when_loaded') === -1) {
|
173
|
+
# current_node.className = current_node.className + ' reactive_record_show_when_loaded';
|
174
|
+
# } else if (current_index == 0 && current_node.className.indexOf('reactive_record_show_while_loading') === -1) {
|
175
|
+
# current_node.className = current_node.className + ' reactive_record_show_while_loading';
|
176
|
+
# }
|
177
|
+
# }
|
178
|
+
# );
|
179
|
+
# }
|
180
|
+
# nil
|
181
|
+
# end
|
182
|
+
#
|
183
|
+
# before_mount do
|
184
|
+
# @uniq_id = WhileLoading.get_next_while_loading_counter
|
185
|
+
# WhileLoading.preload_css(
|
186
|
+
# ":not(.reactive_record_is_loading).reactive_record_while_loading_container_#{@uniq_id} > :nth-child(1) {\n"+
|
187
|
+
# " display: none;\n"+
|
188
|
+
# "}\n"
|
189
|
+
# )
|
190
|
+
# end
|
191
|
+
#
|
192
|
+
# after_mount do
|
193
|
+
# WhileLoading.add_style_sheet
|
194
|
+
# after_mount_and_update
|
195
|
+
# end
|
196
|
+
#
|
197
|
+
# after_update :after_mount_and_update
|
198
|
+
#
|
199
|
+
# render do
|
200
|
+
# @RenderOriginalChild.call(@uniq_id)
|
201
|
+
# end
|
202
|
+
#
|
203
|
+
# end
|
204
|
+
#
|
205
|
+
# end
|
206
|
+
#
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# module Hyperstack
|
210
|
+
# module Component
|
211
|
+
#
|
212
|
+
# class Element
|
213
|
+
#
|
214
|
+
# def while_loading(display = "", &loading_display_block)
|
215
|
+
# original_block = block || -> () {}
|
216
|
+
#
|
217
|
+
# if display.respond_to? :as_node
|
218
|
+
# display = display.as_node
|
219
|
+
# block = lambda { display.render; instance_eval(&original_block) }
|
220
|
+
# elsif !loading_display_block
|
221
|
+
# block = lambda { display; instance_eval(&original_block) }
|
222
|
+
# else
|
223
|
+
# block = ->() { instance_eval(&loading_display_block); instance_eval(&original_block) }
|
224
|
+
# end
|
225
|
+
# loading_child = Internal::Component::RenderingContext.build do |buffer|
|
226
|
+
# Hyperstack::Internal::Component::RenderingContext.render(:span, key: 1, &loading_display_block) #{ to_s }
|
227
|
+
# buffer.dup
|
228
|
+
# end
|
229
|
+
# children = `#{@native}.props.children.slice(0)`
|
230
|
+
# children.unshift(loading_child[0].instance_eval { @native })
|
231
|
+
# @native = `React.cloneElement(#{@native}, #{@properties.shallow_to_n}, #{children})`
|
232
|
+
# render_original_child = lambda do |uniq_id|
|
233
|
+
# classes = [
|
234
|
+
# @properties[:class], @properties[:className],
|
235
|
+
# "reactive_record_while_loading_container_#{uniq_id}"
|
236
|
+
# ].compact.join(' ')
|
237
|
+
# @properties.merge!({
|
238
|
+
# "data-reactive_record_while_loading_container_id" => uniq_id,
|
239
|
+
# "data-reactive_record_enclosing_while_loading_container_id" => uniq_id,
|
240
|
+
# className: classes
|
241
|
+
# })
|
242
|
+
# @native = `React.cloneElement(#{@native}, #{@properties.shallow_to_n})`
|
243
|
+
# render
|
244
|
+
# end
|
245
|
+
# delete
|
246
|
+
# ReactAPI.create_element(
|
247
|
+
# ReactiveRecord::WhileLoading,
|
248
|
+
# loading: waiting_on_resources,
|
249
|
+
# render_original_child: render_original_child)
|
250
|
+
# end
|
251
|
+
#
|
252
|
+
# def hide_while_loading
|
253
|
+
# while_loading
|
254
|
+
# end
|
255
|
+
#
|
256
|
+
# end
|
257
|
+
# end
|
258
|
+
# end
|
259
|
+
#
|
260
|
+
|
261
|
+
|
77
262
|
class WhileLoading
|
78
263
|
|
79
264
|
include Hyperstack::Component::IsomorphicHelpers
|
@@ -92,7 +277,7 @@ module ReactiveRecord
|
|
92
277
|
end
|
93
278
|
|
94
279
|
def self.has_observers?
|
95
|
-
Hyperstack::Internal::
|
280
|
+
Hyperstack::Internal::State::Variable.observed?(self, :loaded_at)
|
96
281
|
end
|
97
282
|
|
98
283
|
class << self
|
@@ -116,6 +301,7 @@ module ReactiveRecord
|
|
116
301
|
param :loading_children
|
117
302
|
param :element_type
|
118
303
|
param :element_props
|
304
|
+
others :other_props
|
119
305
|
param :display, default: ''
|
120
306
|
|
121
307
|
class << self
|
@@ -126,28 +312,28 @@ module ReactiveRecord
|
|
126
312
|
|
127
313
|
def loading!
|
128
314
|
Hyperstack::Internal::Component::RenderingContext.waiting_on_resources = true
|
129
|
-
Hyperstack::Internal::
|
315
|
+
Hyperstack::Internal::State::Variable.get(self, :loaded_at)
|
130
316
|
# this was moved to where the fetch is actually pushed on to the fetch array in isomorphic base
|
131
|
-
# Hyperstack::Internal::
|
317
|
+
# Hyperstack::Internal::State::Variable.set(self, :quiet, false)
|
132
318
|
@is_loading = true
|
133
319
|
end
|
134
320
|
|
135
321
|
def loaded_at(loaded_at)
|
136
|
-
Hyperstack::Internal::
|
322
|
+
Hyperstack::Internal::State::Variable.set(self, :loaded_at, loaded_at)
|
137
323
|
@is_loading = false
|
138
324
|
end
|
139
325
|
|
140
326
|
def quiet?
|
141
|
-
Hyperstack::Internal::
|
327
|
+
Hyperstack::Internal::State::Variable.get(self, :quiet)
|
142
328
|
end
|
143
329
|
|
144
330
|
def page_loaded?
|
145
|
-
Hyperstack::Internal::
|
331
|
+
Hyperstack::Internal::State::Variable.get(self, :page_loaded)
|
146
332
|
end
|
147
333
|
|
148
334
|
def quiet!
|
149
|
-
Hyperstack::Internal::
|
150
|
-
after(1) { Hyperstack::Internal::
|
335
|
+
Hyperstack::Internal::State::Variable.set(self, :quiet, true)
|
336
|
+
after(1) { Hyperstack::Internal::State::Variable.set(self, :page_loaded, true) } unless on_opal_server? or @page_loaded
|
151
337
|
@page_loaded = true
|
152
338
|
end
|
153
339
|
|
@@ -157,8 +343,8 @@ module ReactiveRecord
|
|
157
343
|
%x{
|
158
344
|
var style_el = document.createElement("style");
|
159
345
|
style_el.setAttribute("type", "text/css");
|
160
|
-
style_el.innerHTML = ".reactive_record_is_loading > .reactive_record_show_when_loaded { display: none; }\n" +
|
161
|
-
".reactive_record_is_loaded > .reactive_record_show_while_loading { display: none; }";
|
346
|
+
style_el.innerHTML = ".reactive_record_is_loading > .reactive_record_show_when_loaded { display: none !important; }\n" +
|
347
|
+
".reactive_record_is_loaded > .reactive_record_show_while_loading { display: none !important; }";
|
162
348
|
document.head.append(style_el);
|
163
349
|
}
|
164
350
|
@style_sheet_added = true
|
@@ -167,53 +353,57 @@ module ReactiveRecord
|
|
167
353
|
|
168
354
|
end
|
169
355
|
|
170
|
-
|
171
|
-
@uniq_id = WhileLoading.get_next_while_loading_counter
|
172
|
-
WhileLoading.preload_css(
|
173
|
-
".reactive_record_while_loading_container_#{@uniq_id} > :nth-child(1n+#{@LoadedChildren.count+1}) {\n"+
|
174
|
-
" display: none;\n"+
|
175
|
-
"}\n"
|
176
|
-
)
|
177
|
-
end
|
178
|
-
|
179
|
-
after_mount do
|
356
|
+
def after_mount_and_update(*)
|
180
357
|
@waiting_on_resources = @Loading
|
181
|
-
WhileLoading.add_style_sheet
|
182
358
|
node = dom_node
|
183
359
|
%x{
|
184
|
-
|
185
|
-
nodes.forEach(
|
360
|
+
Array.from(node.children).forEach(
|
186
361
|
function(current_node, current_index, list_obj) {
|
187
|
-
if (current_node.className.indexOf('reactive_record_show_when_loaded') === -1) {
|
362
|
+
if (current_index > 0 && current_node.className.indexOf('reactive_record_show_when_loaded') === -1) {
|
188
363
|
current_node.className = current_node.className + ' reactive_record_show_when_loaded';
|
189
|
-
}
|
190
|
-
}
|
191
|
-
);
|
192
|
-
nodes = node.querySelectorAll(':nth-child(1n+'+#{@LoadedChildren.count+1}+')');
|
193
|
-
nodes.forEach(
|
194
|
-
function(current_node, current_index, list_obj) {
|
195
|
-
if (current_node.className.indexOf('reactive_record_show_while_loading') === -1) {
|
364
|
+
} else if (current_index == 0 && current_node.className.indexOf('reactive_record_show_while_loading') === -1) {
|
196
365
|
current_node.className = current_node.className + ' reactive_record_show_while_loading';
|
197
366
|
}
|
198
367
|
}
|
199
368
|
);
|
200
369
|
}
|
370
|
+
nil
|
201
371
|
end
|
202
372
|
|
203
|
-
|
204
|
-
@
|
373
|
+
before_mount do
|
374
|
+
@uniq_id = WhileLoading.get_next_while_loading_counter
|
375
|
+
WhileLoading.preload_css(
|
376
|
+
":not(.reactive_record_is_loading).reactive_record_while_loading_container_#{@uniq_id} > :nth-child(1) {\n"+
|
377
|
+
" display: none;\n"+
|
378
|
+
"}\n"
|
379
|
+
)
|
380
|
+
end
|
381
|
+
|
382
|
+
after_mount do
|
383
|
+
WhileLoading.add_style_sheet
|
384
|
+
after_mount_and_update
|
205
385
|
end
|
206
386
|
|
207
|
-
|
387
|
+
after_update :after_mount_and_update
|
388
|
+
|
389
|
+
render do
|
390
|
+
# return ReactAPI.create_element(@ElementType[0], @ElementProps.dup) do
|
391
|
+
# @LoadedChildren
|
392
|
+
# end
|
208
393
|
props = @ElementProps.dup
|
209
|
-
classes = [
|
394
|
+
classes = [
|
395
|
+
props[:class], props[:className],
|
396
|
+
@OtherProps.delete(:class), @OtherProps.delete(:className),
|
397
|
+
"reactive_record_while_loading_container_#{@uniq_id}"
|
398
|
+
].compact.join(" ")
|
210
399
|
props.merge!({
|
211
400
|
"data-reactive_record_while_loading_container_id" => @uniq_id,
|
212
401
|
"data-reactive_record_enclosing_while_loading_container_id" => @uniq_id,
|
213
402
|
class: classes
|
214
403
|
})
|
404
|
+
props.merge!(@OtherProps)
|
215
405
|
ReactAPI.create_element(@ElementType[0], props) do
|
216
|
-
@
|
406
|
+
@LoadingChildren + @LoadedChildren
|
217
407
|
end.tap { |e| e.waiting_on_resources = @Loading }
|
218
408
|
end
|
219
409
|
|
@@ -231,6 +421,10 @@ module Hyperstack
|
|
231
421
|
def while_loading(display = "", &loading_display_block)
|
232
422
|
loaded_children = []
|
233
423
|
loaded_children = block.call.dup if block
|
424
|
+
if loaded_children.last.is_a? String
|
425
|
+
loaded_children <<
|
426
|
+
Hyperstack::Internal::Component::ReactWrapper.create_element(:span) { loaded_children.pop }
|
427
|
+
end
|
234
428
|
if display.respond_to? :as_node
|
235
429
|
display = display.as_node
|
236
430
|
loading_display_block = lambda { display.render }
|
@@ -238,12 +432,10 @@ module Hyperstack
|
|
238
432
|
loading_display_block = lambda { display }
|
239
433
|
end
|
240
434
|
loading_children = Internal::Component::RenderingContext.build do |buffer|
|
241
|
-
|
242
|
-
result = result.to_s if result.try :acts_as_string?
|
243
|
-
result.span.tap { |e| e.waiting_on_resources = Internal::Component::RenderingContext.waiting_on_resources } if result.is_a? String
|
435
|
+
Hyperstack::Internal::Component::RenderingContext.render(:span, &loading_display_block) #{ to_s }
|
244
436
|
buffer.dup
|
245
437
|
end
|
246
|
-
|
438
|
+
as_node
|
247
439
|
new_element = ReactAPI.create_element(
|
248
440
|
ReactiveRecord::WhileLoading,
|
249
441
|
loading: waiting_on_resources,
|
@@ -252,7 +444,7 @@ module Hyperstack
|
|
252
444
|
element_type: [type],
|
253
445
|
element_props: properties)
|
254
446
|
|
255
|
-
Internal::Component::RenderingContext.replace(self, new_element)
|
447
|
+
#Internal::Component::RenderingContext.replace(self, new_element)
|
256
448
|
end
|
257
449
|
|
258
450
|
def hide_while_loading
|
@@ -267,6 +459,10 @@ if RUBY_ENGINE == 'opal'
|
|
267
459
|
module Hyperstack
|
268
460
|
module Component
|
269
461
|
|
462
|
+
def quiet?
|
463
|
+
Hyperstack::Internal::State::Variable.get(ReactiveRecord::WhileLoading, :quiet)
|
464
|
+
end
|
465
|
+
|
270
466
|
alias_method :original_component_did_mount, :component_did_mount
|
271
467
|
|
272
468
|
def component_did_mount(*args)
|
@@ -282,6 +478,27 @@ if RUBY_ENGINE == 'opal'
|
|
282
478
|
reactive_record_link_set_while_loading_container_class
|
283
479
|
end
|
284
480
|
|
481
|
+
# This is required to support legacy browsers (Internet Explorer 9+)
|
482
|
+
# https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
|
483
|
+
`
|
484
|
+
if (typeof(Element) != 'undefined' && !Element.prototype.matches) {
|
485
|
+
Element.prototype.matches = Element.prototype.msMatchesSelector ||
|
486
|
+
Element.prototype.webkitMatchesSelector;
|
487
|
+
}
|
488
|
+
|
489
|
+
if (typeof(Element) != 'undefined' && !Element.prototype.closest) {
|
490
|
+
Element.prototype.closest = function(s) {
|
491
|
+
var el = this;
|
492
|
+
|
493
|
+
do {
|
494
|
+
if (el.matches(s)) return el;
|
495
|
+
el = el.parentElement || el.parentNode;
|
496
|
+
} while (el !== null && el.nodeType === 1);
|
497
|
+
return null;
|
498
|
+
};
|
499
|
+
}
|
500
|
+
`
|
501
|
+
|
285
502
|
def reactive_record_link_to_enclosing_while_loading_container
|
286
503
|
# Call after any component mounts - attaches the containers loading id to this component
|
287
504
|
# Fyi, the while_loading container is responsible for setting its own link to itself
|