reactive-record 0.7.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.
@@ -0,0 +1,262 @@
1
+ module ReactiveRecord
2
+
3
+ # will repeatedly execute the block until it is loaded
4
+ # immediately returns a promise that will resolve once the block is loaded
5
+
6
+ def self.load(&block)
7
+ promise = Promise.new
8
+ @load_stack ||= []
9
+ @load_stack << @loads_pending
10
+ @loads_pending = nil
11
+ result = block.call
12
+ if @loads_pending
13
+ @blocks_to_load ||= []
14
+ @blocks_to_load << [promise, block]
15
+ else
16
+ promise.resolve result
17
+ end
18
+ @loads_pending = @load_stack.pop
19
+ promise
20
+ end
21
+
22
+ def self.loads_pending!
23
+ @loads_pending = true
24
+ end
25
+
26
+ def self.run_blocks_to_load
27
+ if @blocks_to_load
28
+ blocks_to_load = @blocks_to_load
29
+ @blocks_to_load = []
30
+ blocks_to_load.each do |promise_and_block|
31
+ @loads_pending = nil
32
+ result = promise_and_block.last.call
33
+ if @loads_pending
34
+ @blocks_to_load << promise_and_block
35
+ else
36
+ promise_and_block.first.resolve result
37
+ end
38
+ end
39
+ end
40
+ rescue Exception => e
41
+ message = "ReactiveRecord.run_blocks_to_load exception raised: #{e}"
42
+ `console.error(#{message})`
43
+ end
44
+
45
+
46
+ # Adds while_loading feature to React
47
+ # to use attach a .while_loading handler to any element for example
48
+ # div { "displayed if everything is loaded" }.while_loading { "displayed while I'm loading" }
49
+ # the contents of the div will be switched (using jQuery.show/hide) depending on the state of contents of the first block
50
+
51
+ # To notify React that something is loading use React::WhileLoading.loading!
52
+ # once everything is loaded then do React::WhileLoading.loaded_at message (typically a time stamp just for debug purposes)
53
+
54
+ class WhileLoading
55
+
56
+ include React::IsomorphicHelpers
57
+
58
+ before_first_mount do
59
+ @css_to_preload = ""
60
+ @while_loading_counter = 0
61
+ end
62
+
63
+ def get_next_while_loading_counter
64
+ @while_loading_counter += 1
65
+ end
66
+
67
+ def preload_css(css)
68
+ @css_to_preload << css << "\n"
69
+ end
70
+
71
+ prerender_footer do
72
+ "<style>\n#{@css_to_preload}\n</style>".tap { @css_to_preload = ""}
73
+ end
74
+
75
+ if RUBY_ENGINE == 'opal'
76
+
77
+ # I DONT THINK WE USE opal-jquery in this module anymore - require 'opal-jquery' if opal_client?
78
+
79
+ include React::Component
80
+
81
+ required_param :loading
82
+ required_param :loaded_children
83
+ required_param :loading_children
84
+ required_param :element_type
85
+ required_param :element_props
86
+ optional_param :display, default: ""
87
+
88
+ class << self
89
+
90
+ def loading?
91
+ @is_loading
92
+ end
93
+
94
+ def loading!
95
+ React::RenderingContext.waiting_on_resources = true
96
+ React::State.get_state(self, :loaded_at)
97
+ @is_loading = true
98
+ end
99
+
100
+ def loaded_at(loaded_at)
101
+ React::State.set_state(self, :loaded_at, loaded_at)
102
+ @is_loading = false
103
+ end
104
+
105
+ def add_style_sheet
106
+ @style_sheet ||= %x{
107
+ $('<style type="text/css">'+
108
+ ' .reactive_record_is_loading > .reactive_record_show_when_loaded { display: none; }'+
109
+ ' .reactive_record_is_loaded > .reactive_record_show_while_loading { display: none; }'+
110
+ '</style>').appendTo("head")
111
+ }
112
+ end
113
+
114
+ end
115
+
116
+ before_mount do
117
+ @uniq_id = WhileLoading.get_next_while_loading_counter
118
+ WhileLoading.preload_css(
119
+ ".reactive_record_while_loading_container_#{@uniq_id} > :nth-child(1n+#{loaded_children.count+1}) {\n"+
120
+ " display: none;\n"+
121
+ "}\n"
122
+ )
123
+ end
124
+
125
+ after_mount do
126
+ @waiting_on_resources = loading
127
+ WhileLoading.add_style_sheet
128
+ %x{
129
+ var node = #{@native}.getDOMNode();
130
+ $(node).children(':nth-child(-1n+'+#{loaded_children.count}+')').addClass('reactive_record_show_when_loaded');
131
+ $(node).children(':nth-child(1n+'+#{loaded_children.count+1}+')').addClass('reactive_record_show_while_loading');
132
+ }
133
+ end
134
+
135
+ after_update do
136
+ @waiting_on_resources = loading
137
+ end
138
+
139
+ def render
140
+ props = element_props.dup
141
+ classes = [props[:class], props[:className], "reactive_record_while_loading_container_#{@uniq_id}"].compact.join(" ")
142
+ props.merge!({
143
+ "data-reactive_record_while_loading_container_id" => @uniq_id,
144
+ "data-reactive_record_enclosing_while_loading_container_id" => @uniq_id,
145
+ class: classes
146
+ })
147
+ React.create_element(element_type, props) { loaded_children + loading_children }
148
+ end
149
+
150
+ end
151
+
152
+ end
153
+
154
+ end
155
+
156
+ module React
157
+
158
+ class Element
159
+
160
+ def while_loading(display = "", &loading_display_block)
161
+
162
+ loaded_children = []
163
+ loaded_children = block.call.dup if block
164
+
165
+ loading_children = [display]
166
+ loading_children = RenderingContext.build do |buffer|
167
+ result = loading_display_block.call
168
+ buffer << result.to_s if result.is_a? String
169
+ buffer.dup
170
+ end if loading_display_block
171
+ RenderingContext.replace(
172
+ self,
173
+ React.create_element(
174
+ ReactiveRecord::WhileLoading,
175
+ loading: waiting_on_resources,
176
+ loading_children: loading_children,
177
+ loaded_children: loaded_children,
178
+ element_type: type,
179
+ element_props: properties)
180
+ )
181
+ end
182
+
183
+ def hide_while_loading
184
+ while_loading
185
+ end
186
+
187
+ end
188
+
189
+ module Component
190
+
191
+ alias_method :original_component_did_mount, :component_did_mount
192
+
193
+ def component_did_mount(*args)
194
+ original_component_did_mount(*args)
195
+ reactive_record_link_to_enclosing_while_loading_container
196
+ reactive_record_link_set_while_loading_container_class
197
+ end
198
+
199
+ alias_method :original_component_did_update, :component_did_update
200
+
201
+ def component_did_update(*args)
202
+ original_component_did_update(*args)
203
+ reactive_record_link_set_while_loading_container_class
204
+ end
205
+
206
+ def reactive_record_link_to_enclosing_while_loading_container
207
+ # Call after any component mounts - attaches the containers loading id to this component
208
+ # Fyi, the while_loading container is responsible for setting its own link to itself
209
+
210
+ %x{
211
+ var node = #{@native}.getDOMNode();
212
+ if (!$(node).is('[data-reactive_record_enclosing_while_loading_container_id]')) {
213
+ var while_loading_container = $(node).closest('[data-reactive_record_while_loading_container_id]')
214
+ if (while_loading_container.length > 0) {
215
+ var container_id = $(while_loading_container).attr('data-reactive_record_while_loading_container_id')
216
+ $(node).attr('data-reactive_record_enclosing_while_loading_container_id', container_id)
217
+ }
218
+ }
219
+ }
220
+
221
+ end
222
+
223
+ def reactive_record_link_set_while_loading_container_class
224
+
225
+ %x{
226
+
227
+ var node = #{@native}.getDOMNode();
228
+ var while_loading_container_id = $(node).attr('data-reactive_record_enclosing_while_loading_container_id');
229
+ if (while_loading_container_id) {
230
+ var while_loading_container = $('[data-reactive_record_while_loading_container_id='+while_loading_container_id+']');
231
+ var loading = (#{waiting_on_resources} == true);
232
+ if (loading) {
233
+ $(node).addClass('reactive_record_is_loading');
234
+ $(node).removeClass('reactive_record_is_loaded');
235
+ $(while_loading_container).addClass('reactive_record_is_loading');
236
+ $(while_loading_container).removeClass('reactive_record_is_loaded');
237
+
238
+ } else if (!$(node).hasClass('reactive_record_is_loaded')) {
239
+
240
+ if (!$(node).attr('data-reactive_record_while_loading_container_id')) {
241
+ $(node).removeClass('reactive_record_is_loading');
242
+ $(node).addClass('reactive_record_is_loaded');
243
+ }
244
+ if (!$(while_loading_container).hasClass('reactive_record_is_loaded')) {
245
+ var loading_children = $(while_loading_container).
246
+ find('[data-reactive_record_enclosing_while_loading_container_id='+while_loading_container_id+'].reactive_record_is_loading')
247
+ if (loading_children.length == 0) {
248
+ $(while_loading_container).removeClass('reactive_record_is_loading')
249
+ $(while_loading_container).addClass('reactive_record_is_loaded')
250
+ }
251
+ }
252
+
253
+ }
254
+
255
+ }
256
+ }
257
+
258
+ end
259
+
260
+ end if RUBY_ENGINE == 'opal'
261
+
262
+ end
@@ -0,0 +1,13 @@
1
+ #require 'rails'
2
+
3
+ module ReactiveRecord
4
+ class Engine < ::Rails::Engine
5
+ isolate_namespace ReactiveRecord
6
+ config.generators do |g|
7
+ g.test_framework :rspec, :fixture => false
8
+ g.fixture_replacement :factory_girl, :dir => 'spec/factories'
9
+ g.assets false
10
+ g.helper false
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,190 @@
1
+ module Browser
2
+
3
+ # Allows you to create an interval that executes the function every given
4
+ # seconds.
5
+ #
6
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Window.setInterval
7
+ class Interval
8
+ # @!attribute [r] every
9
+ # @return [Float] the seconds every which the block is called
10
+ attr_reader :every
11
+
12
+ # Create and start an interval.
13
+ #
14
+ # @param window [Window] the window to start the interval on
15
+ # @param time [Float] seconds every which to call the block
16
+ def initialize(window, time, &block)
17
+ @window = Native.convert(window)
18
+ @every = time
19
+ @block = block
20
+
21
+ @aborted = false
22
+ end
23
+
24
+ # Check if the interval has been stopped.
25
+ def stopped?
26
+ @id.nil?
27
+ end
28
+
29
+ # Check if the interval has been aborted.
30
+ def aborted?
31
+ @aborted
32
+ end
33
+
34
+ # Abort the interval, it won't be possible to start it again.
35
+ def abort
36
+ `#@window.clearInterval(#@id)`
37
+
38
+ @aborted = true
39
+ @id = nil
40
+ end
41
+
42
+ # Stop the interval, it will be possible to start it again.
43
+ def stop
44
+ return if stopped?
45
+
46
+ `#@window.clearInterval(#@id)`
47
+
48
+ @stopped = true
49
+ @id = nil
50
+ end
51
+
52
+ # Start the interval if it has been stopped.
53
+ def start
54
+ raise "the interval has been aborted" if aborted?
55
+ return unless stopped?
56
+
57
+ @id = `#@window.setInterval(#@block, #@every * 1000)`
58
+ end
59
+
60
+ # Call the [Interval] block.
61
+ def call
62
+ @block.call
63
+ end
64
+ end
65
+
66
+ class Window
67
+ # Execute the block every given seconds.
68
+ #
69
+ # @param time [Float] the seconds between every call
70
+ #
71
+ # @return [Interval] the object representing the interval
72
+ def every(time, &block)
73
+ Interval.new(@native, time, &block).tap(&:start)
74
+ end
75
+
76
+ # Execute the block every given seconds, you have to call [#start] on it
77
+ # yourself.
78
+ #
79
+ # @param time [Float] the seconds between every call
80
+ #
81
+ # @return [Interval] the object representing the interval
82
+ def every!(time, &block)
83
+ Interval.new(@native, time, &block)
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ module Kernel
90
+ # (see Browser::Window#every)
91
+ def every(time, &block)
92
+ $window.every(time, &block)
93
+ end
94
+
95
+ # (see Browser::Window#every!)
96
+ def every!(time, &block)
97
+ $window.every!(time, &block)
98
+ end
99
+ end
100
+
101
+ class Proc
102
+ # (see Browser::Window#every)
103
+ def every(time)
104
+ $window.every(time, &self)
105
+ end
106
+
107
+ # (see Browser::Window#every!)
108
+ def every!(time)
109
+ $window.every!(time, &self)
110
+ end
111
+ end
112
+
113
+ module Browser
114
+
115
+ # Allows you to delay the call to a function which gets called after the
116
+ # given time.
117
+ #
118
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout
119
+ class Delay
120
+ # @!attribute [r] after
121
+ # @return [Float] the seconds after which the block is called
122
+ attr_reader :after
123
+
124
+ # Create and start a timeout.
125
+ #
126
+ # @param window [Window] the window to start the timeout on
127
+ # @param time [Float] seconds after which the block is called
128
+ def initialize(window, time, &block)
129
+ @window = Native.convert(window)
130
+ @after = time
131
+ @block = block
132
+ end
133
+
134
+ # Abort the timeout.
135
+ def abort
136
+ `#@window.clearTimeout(#@id)`
137
+ end
138
+
139
+ # Start the delay.
140
+ def start
141
+ @id = `#@window.setTimeout(#{@block.to_n}, #@after * 1000)`
142
+ end
143
+ end
144
+
145
+ class Window
146
+ # Execute a block after the given seconds.
147
+ #
148
+ # @param time [Float] the seconds after it gets called
149
+ #
150
+ # @return [Delay] the object representing the timeout
151
+ def after(time, &block)
152
+ Delay.new(@native, time, &block).tap(&:start)
153
+ end
154
+
155
+ # Execute a block after the given seconds, you have to call [#start] on it
156
+ # yourself.
157
+ #
158
+ # @param time [Float] the seconds after it gets called
159
+ #
160
+ # @return [Delay] the object representing the timeout
161
+ def after!(time, &block)
162
+ Delay.new(@native, time, &block)
163
+ end
164
+ end
165
+
166
+ end
167
+
168
+ module Kernel
169
+ # (see Browser::Window#after)
170
+ def after(time, &block)
171
+ `setTimeout(#{block.to_n}, time * 1000)`
172
+ end
173
+
174
+ # (see Browser::Window#after!)
175
+ def after!(time, &block)
176
+ `setTimeout(#{block.to_n}, time * 1000)`
177
+ end
178
+ end
179
+
180
+ class Proc
181
+ # (see Browser::Window#after)
182
+ def after(time)
183
+ $window.after(time, &self)
184
+ end
185
+
186
+ # (see Browser::Window#after!)
187
+ def after!(time)
188
+ $window.after!(time, &self)
189
+ end
190
+ end